import 'server-only'

import { createHash, randomInt, timingSafeEqual } from 'node:crypto'
import { createAdminClient } from '@/lib/supabase/admin'
import { sendPlatformEmail } from '@/lib/email/send'
import { logSecurityEvent } from '@/lib/security/events'

/**
 * One-time email codes (2026-09-22, §11, §27): six digits, hashed at rest,
 * 10 minutes, 5 attempts, at most 3 emails per address (and per IP) in
 * 15 minutes. The email goes through the transactional stream
 * (account_security — never blocked by marketing opt-outs).
 */
const TTL_MIN = 10
const MAX_ATTEMPTS = 5
const missing = (e: { message?: string } | null | undefined) => !!e && /does not exist|schema cache/i.test(e.message ?? '')
const hash = (email: string, code: string) => createHash('sha256').update(`${email}:${code}`).digest('hex')

export async function issueEmailCode(params: { email: string; ip: string | null; purpose?: string; perWindow?: number; origin?: string }) {
  const admin = createAdminClient()
  const email = params.email.trim().toLowerCase()
  const since = new Date(Date.now() - 15 * 60_000).toISOString()
  const limit = params.perWindow ?? 3
  const [byEmail, byIp] = await Promise.all([
    admin.from('email_verification_codes').select('id', { count: 'exact', head: true }).eq('email_normalized', email).gte('created_at', since),
    params.ip ? admin.from('email_verification_codes').select('id', { count: 'exact', head: true }).eq('ip', params.ip).gte('created_at', since) : Promise.resolve({ count: 0, error: null }),
  ])
  if (missing(byEmail.error)) return { ok: false as const, error: 'Email sign-in needs database migration 0029.' }
  if ((byEmail.count ?? 0) >= limit || (byIp.count ?? 0) >= limit * 3) {
    await logSecurityEvent({ event: 'login_locked', username: email, ip: params.ip, detail: { step: 'email_code', reason: 'too_many_codes' } })
    return { ok: false as const, error: 'Too many codes requested. Wait 15 minutes and try again.' }
  }
  const code = String(randomInt(0, 1_000_000)).padStart(6, '0')
  const { error } = await admin.from('email_verification_codes').insert({ email_normalized: email, code_hash: hash(email, code), purpose: params.purpose ?? 'intake_signin', ip: params.ip, expires_at: new Date(Date.now() + TTL_MIN * 60_000).toISOString() })
  if (error) return { ok: false as const, error: missing(error) ? 'Email sign-in needs database migration 0029.' : error.message }
  const sent = await sendPlatformEmail({ templateKey: 'email_code', to: email, data: { verification_code: code, code_minutes: String(TTL_MIN) }, clean: true, origin: params.origin })
  if (sent.status === 'suppressed') return { ok: false as const, error: `We cannot send email to ${email} (${sent.reason?.replace(/_/g, ' ')}). Use a different address.` }
  if (sent.status === 'failed') return { ok: false as const, error: `Could not send the code: ${sent.reason}` }
  await logSecurityEvent({ event: 'login_mfa_required', username: email, ip: params.ip, detail: { step: 'email_code_sent', status: sent.status } })
  // Recorded (no provider): surface the code only in development so the flow can be exercised.
  return { ok: true as const, status: sent.status, devCode: sent.status === 'recorded_not_delivered' && process.env.NODE_ENV !== 'production' ? code : undefined }
}

export async function verifyEmailCode(params: { email: string; code: string; ip: string | null; purpose?: string }) {
  const admin = createAdminClient()
  const email = params.email.trim().toLowerCase()
  const given = params.code.replace(/\D/g, '')
  const { data: rows, error } = await admin.from('email_verification_codes').select('*').eq('email_normalized', email).eq('purpose', params.purpose ?? 'intake_signin').is('consumed_at', null).gt('expires_at', new Date().toISOString()).order('created_at', { ascending: false }).limit(1)
  if (missing(error)) return { ok: false as const, error: 'Email sign-in needs database migration 0029.' }
  const row = rows?.[0]
  if (!row) return { ok: false as const, error: 'That code has expired. Request a new one.' }
  if (row.attempts >= MAX_ATTEMPTS) return { ok: false as const, error: 'Too many attempts. Request a new code.' }
  const expected = row.code_hash as string, actual = hash(email, given)
  if (given.length !== 6 || expected.length !== actual.length || !timingSafeEqual(Buffer.from(expected), Buffer.from(actual))) {
    await admin.from('email_verification_codes').update({ attempts: row.attempts + 1 }).eq('id', row.id)
    await logSecurityEvent({ event: 'login_mfa_failed', username: email, ip: params.ip, detail: { step: 'email_code' } })
    return { ok: false as const, error: 'That code is not correct.' }
  }
  await admin.from('email_verification_codes').update({ consumed_at: new Date().toISOString() }).eq('id', row.id)
  return { ok: true as const }
}
