import { randomBytes, createHash, scrypt, timingSafeEqual } from "node:crypto";
import { promisify } from "node:util";
import { DB } from "./database.ts";
const derive = promisify(scrypt);
export const adminCookieName = "institution_admin_session";
export type AdminUser = {
  userId: string;
  displayName: string;
  email: string;
  role: "ADMIN" | "VIEWER";
};
export const tokenHash = (token: string) =>
  createHash("sha256").update(token).digest("hex");
export async function hashPassword(password: string) {
  const salt = randomBytes(16).toString("hex");
  const key = (await derive(password, salt, 64)) as Buffer;
  return `scrypt:${salt}:${key.toString("hex")}`;
}
export async function verifyPassword(password: string, encoded: string) {
  const [algorithm, salt, hash] = encoded.split(":");
  if (algorithm !== "scrypt" || !salt || !/^[a-f0-9]{128}$/.test(hash || ""))
    return false;
  const actual = (await derive(password, salt, 64)) as Buffer;
  return timingSafeEqual(actual, Buffer.from(hash, "hex"));
}
const dummy = "scrypt:00000000000000000000000000000000:" + "0".repeat(128);
export async function login(username: string, password: string) {
  const user = await DB.prepare(
    "SELECT id,password_hash FROM admin_users WHERE username=? AND active=1",
  )
    .bind(username)
    .first<{ id: string; password_hash: string }>();
  const valid = await verifyPassword(password, user?.password_hash || dummy);
  if (!user || !valid) return null;
  const token = randomBytes(32).toString("base64url"),
    expires = new Date(Date.now() + 8 * 3600000);
  await DB.prepare(
    "INSERT INTO admin_sessions(token_hash,user_id,expires_at) VALUES (?,?,?)",
  )
    .bind(tokenHash(token), user.id, expires.toISOString())
    .run();
  return { token, expires, userId: user.id };
}
export async function sessionUser(
  token: string | undefined,
): Promise<AdminUser | null> {
  if (!token || !/^[A-Za-z0-9_-]{43}$/.test(token)) return null;
  return DB.prepare(
    "SELECT u.id userId,u.display_name displayName,u.email,u.role FROM admin_sessions s JOIN admin_users u ON u.id=s.user_id WHERE s.token_hash=? AND s.expires_at>? AND u.active=1",
  )
    .bind(tokenHash(token), new Date().toISOString())
    .first<AdminUser>();
}
export async function revokeSession(token: string) {
  await DB.prepare("DELETE FROM admin_sessions WHERE token_hash=?")
    .bind(tokenHash(token))
    .run();
}
export function sessionCookie(value: string, expires: Date) {
  const secure =
    new URL(process.env.APP_URL || "http://localhost:3000").protocol ===
    "https:";
  return `${adminCookieName}=${value}; Path=/; HttpOnly; ${secure ? "Secure; " : ""}SameSite=Lax; Expires=${expires.toUTCString()}`;
}
