/** Admin authentication helpers for CultRoll admin pages */

export const ADMIN_COOKIE = 'cr-admin';
export const ADMIN_COOKIE_MAX_AGE = 60 * 60 * 8; // 8 hours

const enc = new TextEncoder();

export async function hashPassword(password: string, salt: string): Promise<string> {
  const keyMaterial = await crypto.subtle.importKey(
    'raw', enc.encode(password), 'PBKDF2', false, ['deriveBits'],
  );
  const bits = await crypto.subtle.deriveBits(
    { name: 'PBKDF2', hash: 'SHA-256', salt: enc.encode(salt), iterations: 100_000 },
    keyMaterial, 256,
  );
  return Array.from(new Uint8Array(bits)).map(b => b.toString(16).padStart(2, '0')).join('');
}

export async function verifyPassword(password: string, db: D1Database): Promise<boolean> {
  const row = await db.prepare(
    "SELECT value FROM settings WHERE key IN ('admin_password_hash','admin_password_salt') ORDER BY key",
  ).all();
  const map: Record<string, string> = {};
  for (const r of row.results as { value: string; key?: string }[]) {
    // key col not in SELECT — query by key IN, order alphabetically: hash, salt
  }
  const hash = await db.prepare("SELECT value FROM settings WHERE key='admin_password_hash'").first<{ value: string }>();
  const saltRow = await db.prepare("SELECT value FROM settings WHERE key='admin_password_salt'").first<{ value: string }>();
  if (!hash || !saltRow) return false;
  const computed = await hashPassword(password, saltRow.value);
  return computed === hash.value;
}

export async function setPassword(newPassword: string, db: D1Database): Promise<void> {
  const salt = Array.from(crypto.getRandomValues(new Uint8Array(16)))
    .map(b => b.toString(16).padStart(2, '0')).join('');
  const hash = await hashPassword(newPassword, salt);
  const now = new Date().toISOString();
  await db.batch([
    db.prepare("INSERT OR REPLACE INTO settings (key,value,updated_at) VALUES ('admin_password_hash',?,?)").bind(hash, now),
    db.prepare("INSERT OR REPLACE INTO settings (key,value,updated_at) VALUES ('admin_password_salt',?,?)").bind(salt, now),
  ]);
}

/** Cookie token = HMAC-SHA256 of "cultroll-admin:{timestamp}" with password hash as key */
export async function makeSessionToken(db: D1Database): Promise<string> {
  const hashRow = await db.prepare("SELECT value FROM settings WHERE key='admin_password_hash'").first<{ value: string }>();
  const secret = hashRow?.value ?? 'fallback';
  const payload = `cultroll-admin:${Math.floor(Date.now() / 1000)}`;
  const key = await crypto.subtle.importKey('raw', enc.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
  const sig = await crypto.subtle.sign('HMAC', key, enc.encode(payload));
  const sigHex = Array.from(new Uint8Array(sig)).map(b => b.toString(16).padStart(2, '0')).join('');
  return `${btoa(payload)}.${sigHex}`;
}

export async function isAuthenticated(cookieValue: string | undefined, db: D1Database): Promise<boolean> {
  if (!cookieValue) return false;
  const [payloadB64, sigHex] = cookieValue.split('.');
  if (!payloadB64 || !sigHex) return false;
  try {
    const payload = atob(payloadB64);
    if (!payload.startsWith('cultroll-admin:')) return false;
    const ts = parseInt(payload.split(':')[1], 10);
    if (Date.now() / 1000 - ts > ADMIN_COOKIE_MAX_AGE) return false;
    const hashRow = await db.prepare("SELECT value FROM settings WHERE key='admin_password_hash'").first<{ value: string }>();
    const secret = hashRow?.value ?? 'fallback';
    const key = await crypto.subtle.importKey('raw', enc.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['verify']);
    const sigBytes = new Uint8Array(sigHex.match(/.{2}/g)!.map(h => parseInt(h, 16)));
    return await crypto.subtle.verify('HMAC', key, sigBytes, enc.encode(payload));
  } catch {
    return false;
  }
}
