import { createHash, randomInt } from "crypto";
import { SignJWT, jwtVerify } from "jose";
import { query } from "./db";
import { hashAadhaar, normalizeMobile } from "./aadhaar";
import { safeInfo } from "./safe-log";

const MAX_ATTEMPTS = 3;
const OTP_TTL_MINUTES = 10;

function otpHash(otp: string, sessionId: string) {
  return createHash("sha256")
    .update(`${sessionId}:${otp}`)
    .digest("hex");
}

function getSecret() {
  return new TextEncoder().encode(
    process.env.JWT_SECRET || "kumbh-dev-secret",
  );
}

export async function createOtpSession(aadhaar: string, mobile: string) {
  const sessionId = crypto.randomUUID();
  const otp = String(randomInt(100000, 999999));
  const aadhaarHash = hashAadhaar(aadhaar);
  const mobileNorm = normalizeMobile(mobile);
  const expires = new Date(Date.now() + OTP_TTL_MINUTES * 60 * 1000);

  await query(
    `INSERT INTO otp_sessions
      (id, aadhaar_hash, mobile, otp_hash, attempts, verified, expires_at)
     VALUES (:id, :aadhaar_hash, :mobile, :otp_hash, 0, 0, :expires_at)`,
    {
      id: sessionId,
      aadhaar_hash: aadhaarHash,
      mobile: mobileNorm,
      otp_hash: otpHash(otp, sessionId),
      expires_at: expires,
    },
  );

  // SMS gateway stub — in production call gov SMS API here
  if (process.env.OTP_DEV_MODE === "true") {
    safeInfo(`[OTP_DEV] mobile=${mobileNorm} otp=${otp}`);
  }

  return {
    sessionId,
    expiresAt: expires.toISOString(),
    ...(process.env.OTP_DEV_MODE === "true" ? { devOtp: otp } : {}),
  };
}

type OtpRow = {
  id: string;
  aadhaar_hash: string;
  mobile: string;
  otp_hash: string;
  attempts: number;
  verified: number;
  expires_at: Date;
};

export async function verifyOtp(sessionId: string, otp: string) {
  const rows = await query<OtpRow[]>(
    `SELECT * FROM otp_sessions WHERE id = :id LIMIT 1`,
    { id: sessionId },
  );
  const row = Array.isArray(rows) ? rows[0] : undefined;
  if (!row) {
    return { ok: false as const, error: "Session not found. Please request a new OTP." };
  }
  if (row.verified) {
    return { ok: false as const, error: "OTP already used. Continue registration." };
  }
  if (new Date(row.expires_at).getTime() < Date.now()) {
    return { ok: false as const, error: "OTP expired. Please request a new OTP." };
  }
  if (row.attempts >= MAX_ATTEMPTS) {
    return {
      ok: false as const,
      error: "Maximum OTP attempts reached. Please call the helpline or try later.",
      locked: true,
    };
  }

  const match = row.otp_hash === otpHash(otp.trim(), sessionId);
  await query(
    `UPDATE otp_sessions SET attempts = attempts + 1 WHERE id = :id`,
    { id: sessionId },
  );

  if (!match) {
    const remaining = MAX_ATTEMPTS - (row.attempts + 1);
    return {
      ok: false as const,
      error:
        remaining > 0
          ? "The OTP does not match. Would you like a new OTP?"
          : "Maximum OTP attempts reached. Please try again later.",
      remaining,
      locked: remaining <= 0,
    };
  }

  await query(`UPDATE otp_sessions SET verified = 1 WHERE id = :id`, {
    id: sessionId,
  });

  const token = await new SignJWT({
    sid: sessionId,
    aadhaar_hash: row.aadhaar_hash,
    mobile: row.mobile,
  })
    .setProtectedHeader({ alg: "HS256" })
    .setExpirationTime("30m")
    .setIssuedAt()
    .sign(getSecret());

  return {
    ok: true as const,
    token,
    mobile: row.mobile,
    aadhaarHash: row.aadhaar_hash,
  };
}

export async function readSessionToken(token: string) {
  const { payload } = await jwtVerify(token, getSecret());
  return payload as {
    sid: string;
    aadhaar_hash: string;
    mobile: string;
  };
}

export { MAX_ATTEMPTS };
