import crypto from "crypto";
import { SignJWT, jwtVerify } from "jose";

/* Password-reset OTP helpers: 6-digit codes stored only as HMAC hashes,
   plus the short-lived jose tokens that carry the user between the
   identify → verify → reset steps (no account enumeration in URLs). */

export const OTP_TTL_MINUTES = 10;
export const OTP_RESEND_COOLDOWN_SEC = 60;
export const OTP_MAX_ATTEMPTS = 5;
export const OTP_HOURLY_LIMIT = 6;

function secret() {
  return new TextEncoder().encode(process.env.JWT_SECRET ?? "dev-secret");
}

export function generateOtp(): string {
  return String(crypto.randomInt(0, 1_000_000)).padStart(6, "0");
}

export function hashOtp(code: string): string {
  return crypto
    .createHmac("sha256", secret())
    .update(`otp:${code}`)
    .digest("base64url");
}

export function safeEqual(a: string, b: string): boolean {
  const ba = Buffer.from(a);
  const bb = Buffer.from(b);
  return ba.length === bb.length && crypto.timingSafeEqual(ba, bb);
}

/** Signed token binding a userId to a forgot-password step. */
export async function signStepToken(uid: number, step: "identify" | "reset"): Promise<string> {
  return new SignJWT({ uid, step })
    .setProtectedHeader({ alg: "HS256" })
    .setIssuedAt()
    .setExpirationTime(step === "identify" ? "15m" : "10m")
    .sign(secret());
}

export async function verifyStepToken(
  token: string,
  step: "identify" | "reset",
): Promise<number | null> {
  try {
    const { payload } = await jwtVerify(token, secret());
    if (payload.step !== step || typeof payload.uid !== "number") return null;
    return payload.uid;
  } catch {
    return null;
  }
}

/** Where an OTP can be delivered for this account shape (prisma user row). */
export interface OtpDestination {
  email: string | null;
  phone: string | null;
}

export function resolveDestinations(user: {
  email: string | null;
  role: string;
  institutionId: number | null;
  studentProfile?: { guardianPhone: string } | null;
  parentOfStudent?: { guardianPhone: string } | null;
  staff?: { phone: string } | null;
  institution?: { phone: string } | null;
}): OtpDestination {
  let phone: string | null = null;
  if (user.role === "STUDENT") phone = user.studentProfile?.guardianPhone ?? null;
  else if (user.role === "PARENT") phone = user.parentOfStudent?.guardianPhone ?? null;
  else if (user.role === "TEACHER" || user.role === "STAFF") phone = user.staff?.phone ?? null;
  else if (user.role === "INSTITUTION_ADMIN") phone = user.institution?.phone ?? null;
  return { email: user.email ?? null, phone };
}
