import { createHmac, timingSafeEqual } from "crypto";

import { getAuthSecretOrThrow } from "@/lib/runtime-env";

type CaptchaPayload = {
  answer: number;
  exp: number;
};

function encodePayload(payload: CaptchaPayload) {
  return Buffer.from(JSON.stringify(payload)).toString("base64url");
}

function decodePayload(token: string): CaptchaPayload | null {
  try {
    return JSON.parse(Buffer.from(token, "base64url").toString("utf8")) as CaptchaPayload;
  } catch {
    return null;
  }
}

function signToken(encodedPayload: string) {
  return createHmac("sha256", getAuthSecretOrThrow())
    .update(encodedPayload)
    .digest("base64url");
}

export function createPublicCaptcha() {
  const left = Math.floor(Math.random() * 8) + 2;
  const right = Math.floor(Math.random() * 8) + 2;
  const payload = encodePayload({
    answer: left + right,
    exp: Math.floor(Date.now() / 1000) + 60 * 15
  });
  const signature = signToken(payload);

  return {
    question: `${left} + ${right}`,
    token: `${payload}.${signature}`
  };
}

export function verifyPublicCaptcha(token: string, rawAnswer: number) {
  const [payload, signature] = token.split(".");
  if (!payload || !signature) return false;

  const expectedSignature = signToken(payload);
  if (expectedSignature.length !== signature.length) return false;
  if (!timingSafeEqual(Buffer.from(expectedSignature), Buffer.from(signature))) return false;

  const decoded = decodePayload(payload);
  if (!decoded) return false;
  if (decoded.exp < Math.floor(Date.now() / 1000)) return false;

  return decoded.answer === rawAnswer;
}
