import { createHmac, randomBytes, createHash } from 'crypto';
import bcrypt from 'bcryptjs';
import type { AstroCookies } from 'astro';
import {
  createDbSession, getDbSession, deleteDbSession,
  getUserById, getUserPublicById, updateUserLastSeen,
  type UserPublic,
} from './db.js';

const SECRET = process.env.BIFROST_SECRET ?? 'dev-secret-do-not-use-in-production';
const COOKIE  = 'bifrost_session';
const SESSION_DAYS = 7;
const INVITE_DAYS  = 14;

// ── Passwords ────────────────────────────────────────────────────

export function hashPassword(password: string): string {
  return bcrypt.hashSync(password, 12);
}

export function verifyPassword(password: string, hash: string): boolean {
  return bcrypt.compareSync(password, hash);
}

// ── Invite tokens ────────────────────────────────────────────────

/** Returns the URL-safe token (give to user) and its hash (store in DB). */
export function generateInviteToken(): { token: string; tokenHash: string } {
  const raw = randomBytes(20).toString('base64url');
  const mac = createHmac('sha256', SECRET).update(raw).digest('base64url').slice(0, 16);
  const token = `${raw}.${mac}`;
  const tokenHash = createHash('sha256').update(token).digest('hex');
  return { token, tokenHash };
}

export function verifyInviteTokenFormat(token: string): boolean {
  const dot = token.lastIndexOf('.');
  if (dot === -1) return false;
  const raw = token.slice(0, dot);
  const mac = token.slice(dot + 1);
  const expected = createHmac('sha256', SECRET).update(raw).digest('base64url').slice(0, 16);
  return mac === expected && raw.length > 0;
}

export function hashToken(token: string): string {
  return createHash('sha256').update(token).digest('hex');
}

export function inviteExpiresAt(): string {
  const d = new Date();
  d.setDate(d.getDate() + INVITE_DAYS);
  return d.toISOString().replace('T', ' ').slice(0, 19);
}

// ── Sessions ─────────────────────────────────────────────────────

export function createSession(userId: number, cookies: AstroCookies): void {
  const id = randomBytes(32).toString('hex');
  const expires = new Date();
  expires.setDate(expires.getDate() + SESSION_DAYS);
  const expiresAt = expires.toISOString().replace('T', ' ').slice(0, 19);
  createDbSession(id, userId, expiresAt);
  cookies.set(COOKIE, id, {
    httpOnly: true,
    sameSite: 'lax',
    path: '/',
    maxAge: SESSION_DAYS * 24 * 60 * 60,
    secure: process.env.NODE_ENV === 'production',
  });
}

export function getSessionUser(cookies: AstroCookies): UserPublic | null {
  const id = cookies.get(COOKIE)?.value;
  if (!id) return null;
  const session = getDbSession(id);
  if (!session) return null;
  const user = getUserPublicById(session.user_id);
  if (user) updateUserLastSeen(user.id);
  return user;
}

export function clearSession(cookies: AstroCookies): void {
  const id = cookies.get(COOKIE)?.value;
  if (id) deleteDbSession(id);
  cookies.delete(COOKIE, { path: '/' });
}
