import 'server-only'

import { cookies } from 'next/headers'
import { SignJWT, jwtVerify } from 'jose'
import type { UserRole } from '@/types/db'

/** Signed-cookie sessions for .env-based auth. */

export const SESSION_COOKIE = 'hha_session'
const SESSION_DAYS = 7

export interface SessionPayload {
  sub: string // user id (derived from email)
  email: string
  name: string
  role: UserRole
  company: string | null
  /**
   * True when the person who SIGNED IN is internal staff (Task 96).
   * Carried separately from `role` because the pilot role switch replaces
   * `role` with the previewed account: an admin who switches to the driver
   * test account must keep the bar that switches them back, while a customer
   * who signs in as a driver must never see it.
   */
  internal?: boolean
  /** Passwordless sessions must never become internal admin sessions. */
  auth_method?: 'google' | 'email_code'
  /**
   * The account that actually SIGNED IN. Equals `sub` for a normal sign-in;
   * for a pilot role switch it is the admin who switched, so revoking that
   * admin also ends the session they switched into.
   */
  origin_sub?: string
  /**
   * The mode the person is working in (2026-09-24): a membership_roles id, or
   * `account:<role_type>` when the role comes from the account itself. Always
   * re-validated server-side — a cookie never grants a role.
   */
  ctx?: string | null
  /** Admin only: the product view being previewed. Never a membership. */
  preview?: string | null
  /** Issued-at, seconds — set by jose; compared against revocation. */
  iat?: number
  [key: string]: unknown
}

function secretKey(): Uint8Array {
  const secret = process.env.AUTH_SECRET
  if (!secret || secret.length < 16) {
    throw new Error('AUTH_SECRET must be set to a random string of at least 16 characters.')
  }
  return new TextEncoder().encode(secret)
}

export async function createSessionToken(payload: SessionPayload): Promise<string> {
  return new SignJWT(payload)
    .setProtectedHeader({ alg: 'HS256' })
    .setIssuedAt()
    .setExpirationTime(`${SESSION_DAYS}d`)
    .sign(secretKey())
}

export async function verifySessionToken(token: string): Promise<SessionPayload | null> {
  try {
    const { payload } = await jwtVerify(token, secretKey())
    if (!payload.sub || !payload.email) return null
    return payload as unknown as SessionPayload
  } catch {
    return null
  }
}

export async function setSessionCookie(payload: SessionPayload) {
  const token = await createSessionToken(payload)
  const store = await cookies()
  store.set(SESSION_COOKIE, token, {
    httpOnly: true,
    sameSite: 'lax',
    secure: process.env.NODE_ENV === 'production',
    path: '/',
    maxAge: SESSION_DAYS * 24 * 3600,
  })
}

export async function clearSessionCookie() {
  const store = await cookies()
  store.delete(SESSION_COOKIE)
}

export async function readSession(): Promise<SessionPayload | null> {
  const store = await cookies()
  const token = store.get(SESSION_COOKIE)?.value
  if (!token) return null
  return verifySessionToken(token)
}

/* ---------------- MFA pending step (2026-09-22) ---------------- */

export const MFA_PENDING_COOKIE = 'hha_mfa'
const MFA_PENDING_MINUTES = 5

export interface MfaPendingPayload {
  sub: string
  username: string
  next: string | null
  auth_method?: 'google'
  [key: string]: unknown
}

/** Password accepted, second factor still owed: a short-lived signed cookie, never a session. */
export async function setMfaPendingCookie(payload: MfaPendingPayload) {
  const token = await new SignJWT({ ...payload, purpose: 'mfa' })
    .setProtectedHeader({ alg: 'HS256' })
    .setIssuedAt()
    .setExpirationTime(`${MFA_PENDING_MINUTES}m`)
    .sign(secretKey())
  const store = await cookies()
  store.set(MFA_PENDING_COOKIE, token, { httpOnly: true, sameSite: 'lax', secure: process.env.NODE_ENV === 'production', path: '/', maxAge: MFA_PENDING_MINUTES * 60 })
}

export async function readMfaPendingCookie(): Promise<MfaPendingPayload | null> {
  const store = await cookies()
  const token = store.get(MFA_PENDING_COOKIE)?.value
  if (!token) return null
  try {
    const { payload } = await jwtVerify(token, secretKey())
    if (payload.purpose !== 'mfa' || !payload.sub) return null
    return payload as unknown as MfaPendingPayload
  } catch {
    return null
  }
}

export async function clearMfaPendingCookie() {
  const store = await cookies()
  store.delete(MFA_PENDING_COOKIE)
}
