import crypto from "node:crypto";

const CAPTCHA_TTL_MS = 10 * 60 * 1000;
const RATE_LIMIT_WINDOW_MS = 15 * 60 * 1000;
const RATE_LIMIT_MAX_ATTEMPTS = 5;

type CaptchaPayload = {
  answer: string;
  expiresAt: number;
  nonce: string;
};

type RateLimitEntry = {
  count: number;
  resetAt: number;
};

const rateLimitStore = new Map<string, RateLimitEntry>();

function getCaptchaSecret() {
  return process.env.CAPTCHA_SECRET || "local-dev-captcha-secret";
}

function signCaptchaPayload(payload: CaptchaPayload) {
  return crypto
    .createHmac("sha256", getCaptchaSecret())
    .update(`${payload.answer}:${payload.expiresAt}:${payload.nonce}`)
    .digest("hex");
}

function encodeCaptcha(payload: CaptchaPayload) {
  const signature = signCaptchaPayload(payload);
  return Buffer.from(JSON.stringify({ ...payload, signature }), "utf8").toString("base64url");
}

function decodeCaptcha(token: string) {
  try {
    const json = Buffer.from(token, "base64url").toString("utf8");
    const parsed = JSON.parse(json) as CaptchaPayload & { signature: string };
    return parsed;
  } catch {
    return null;
  }
}

export function generateCaptcha() {
  const left = crypto.randomInt(2, 10);
  const right = crypto.randomInt(1, 10);
  const operator = crypto.randomInt(0, 2) === 0 ? "+" : "-";
  const safeLeft = operator === "-" && left < right ? right + crypto.randomInt(1, 4) : left;
  const answer = operator === "+" ? safeLeft + right : safeLeft - right;

  const payload: CaptchaPayload = {
    answer: String(answer),
    expiresAt: Date.now() + CAPTCHA_TTL_MS,
    nonce: crypto.randomBytes(8).toString("hex"),
  };

  return {
    prompt: `${safeLeft} ${operator} ${right} = ?`,
    token: encodeCaptcha(payload),
  };
}

export function verifyCaptcha(token: string, answer: string) {
  const parsed = decodeCaptcha(token);
  if (!parsed) {
    return { ok: false, reason: "Captcha tidak valid." };
  }

  const expectedSignature = signCaptchaPayload({
    answer: parsed.answer,
    expiresAt: parsed.expiresAt,
    nonce: parsed.nonce,
  });

  if (parsed.signature !== expectedSignature) {
    return { ok: false, reason: "Captcha tidak valid." };
  }

  if (Date.now() > parsed.expiresAt) {
    return { ok: false, reason: "Captcha sudah kedaluwarsa. Coba muat ulang captcha." };
  }

  if (parsed.answer.trim() !== answer.trim()) {
    return { ok: false, reason: "Jawaban captcha salah." };
  }

  return { ok: true };
}

export function checkRateLimit(identifier: string) {
  const now = Date.now();
  const existing = rateLimitStore.get(identifier);

  if (!existing || existing.resetAt <= now) {
    rateLimitStore.set(identifier, {
      count: 1,
      resetAt: now + RATE_LIMIT_WINDOW_MS,
    });
    return { ok: true };
  }

  if (existing.count >= RATE_LIMIT_MAX_ATTEMPTS) {
    return { ok: false, retryAfterMs: existing.resetAt - now };
  }

  existing.count += 1;
  rateLimitStore.set(identifier, existing);
  return { ok: true };
}

export function getClientIpHeader(forwardedFor: string | null) {
  if (!forwardedFor) {
    return "unknown";
  }

  return forwardedFor.split(",")[0]?.trim() || "unknown";
}
