import { NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { createAdminClient } from '@/lib/supabase/admin'
import { setSessionCookie } from '@/lib/auth/session'
import { findEnvUserById } from '@/lib/auth/env-users'
import { effectiveRole, getAccountOverride } from '@/lib/auth/accounts'
import { ensureSelfAccount } from '@/lib/auth/self-accounts'
import { issueEmailCode, verifyEmailCode } from '@/lib/security/verification-codes'
import { clientIp, rateLimit } from '@/lib/security/rate-limit'
import { logSecurityEvent } from '@/lib/security/events'
import { linkModeratorGrants, surfacesForEmail } from '@/lib/data/moderator-access'
import { publicOrigin } from '@/lib/app-url'
import { emailPattern } from '@/lib/like'
import { recaptchaHostname, verifyRecaptcha } from '@/lib/security/recaptcha'
import { findHistoricalProfile, claimHistoricalProfile } from '@/lib/data/historical-profiles'
import { HISTORICAL_FOUND_MESSAGE } from '@/lib/domain/historical-claim'

/**
 * Sign in with an emailed code (Nash, 2026-09-23). A moderator invited to one
 * page may have no password — this is how they get in, and it also serves
 * anyone who signed up through a public intake or invitation link.
 *
 * Only an address that already has an account or a moderator grant receives a
 * code; the reply is identical either way so nothing leaks about who exists.
 */
const schema = z.discriminatedUnion('op', [
  z.object({ op: z.literal('start'), email: z.string().trim().email().max(200), captchaToken: z.string().max(4096).nullable().optional() }),
  z.object({ op: z.literal('verify'), email: z.string().trim().email().max(200), code: z.string().trim().min(6).max(8) }),
])
const SAME_ANSWER = { ok: true, message: 'If that address has a HeavyHaul Agent account, a sign-in code is on its way.' }

async function knownAddress(email: string): Promise<{ known: boolean; userId: string | null; name: string | null; role: string | null }> {
  const admin = createAdminClient()
  const { data: profs } = await admin.from('profiles').select('id, full_name, default_role').ilike('email', emailPattern(email)).order('created_at', { ascending: true }).limit(1)
  const p = ((profs ?? [])[0] ?? null) as { id: string; full_name: string | null; default_role: string } | null
  if (p) return { known: true, userId: p.id, name: p.full_name, role: p.default_role }
  const grants = await surfacesForEmail(email)
  return { known: grants.length > 0, userId: null, name: null, role: null }
}

export async function POST(req: NextRequest) {
  const parsed = schema.safeParse(await req.json().catch(() => ({})))
  if (!parsed.success) return NextResponse.json({ error: 'Enter a valid email address.' }, { status: 400 })
  const input = parsed.data
  const ip = clientIp(req.headers)
  const userAgent = req.headers.get('user-agent')
  const email = input.email.toLowerCase()

  if (input.op === 'start') {
    const gate = rateLimit(`login-code:${ip}`, 10, 15 * 60_000)
    if (!gate.allowed) return NextResponse.json({ error: 'Too many requests. Try again in a few minutes.' }, { status: 429 })
    const captcha = await verifyRecaptcha(input.captchaToken, 'email_code_start', recaptchaHostname(req.headers.get('host')))
    if (!captcha.ok) return NextResponse.json({ error: captcha.error }, { status: 403 })
    const who = await knownAddress(email)
    if (!who.known) return NextResponse.json(SAME_ANSWER)
    const r = await issueEmailCode({ email, ip, origin: publicOrigin(req) })
    if (!r.ok) return NextResponse.json({ error: r.error }, { status: /migration/i.test(r.error) ? 503 : /too many/i.test(r.error) ? 429 : 400 })
    // §41: an imported identity is announced, never shown, until the address is proved.
    const historical = await findHistoricalProfile(email)
    const message = historical && historical.profile.claim_status !== 'claimed' ? `${HISTORICAL_FOUND_MESSAGE} ${SAME_ANSWER.message}` : SAME_ANSWER.message
    return NextResponse.json({ ...SAME_ANSWER, message, ...(r.devCode ? { dev_code: r.devCode } : {}) })
  }

  const v = await verifyEmailCode({ email, code: input.code, ip })
  if (!v.ok) return NextResponse.json({ error: v.error }, { status: 400 })
  const who = await knownAddress(email)
  if (!who.known) return NextResponse.json({ error: 'That code is not valid.' }, { status: 400 })

  // An admin-provisioned login signs in as itself; anyone else gets their self-service account.
  const envUser = who.userId ? findEnvUserById(who.userId) : null
  let session: { sub: string; email: string; name: string; role: 'broker' | 'dispatcher' | 'driver' | 'admin'; company: string | null }
  let historical: { connected: boolean; roles: string[] } = { connected: false, roles: [] }
  if (envUser) {
    const role = effectiveRole(envUser, await getAccountOverride(envUser.id))
    if (role === 'admin') return NextResponse.json({ error: 'Admin accounts must use password sign-in.' }, { status: 403 })
    session = { sub: envUser.id, email: envUser.email, name: envUser.name, role, company: envUser.company }
    // The code proved this address for an admin-provisioned login too (§17, §53).
    historical = await claimHistoricalProfile({ userId: envUser.id, email, emailVerified: true, actorLabel: envUser.name })
  } else {
    const r = await ensureSelfAccount({ email, name: who.name, role: (who.role as 'broker' | 'dispatcher' | 'driver' | undefined) ?? 'dispatcher' })
    if (!r.ok) return NextResponse.json({ error: r.error }, { status: 400 })
    if (r.account.blocked_at) return NextResponse.json({ error: 'This account is blocked. Contact support.' }, { status: 403 })
    session = { sub: r.account.id, email: r.account.email, name: r.account.name, role: r.account.role, company: null }
    historical = r.historical
  }
  if (session.role === 'admin') return NextResponse.json({ error: 'Admin accounts must use password sign-in.' }, { status: 403 })
  await setSessionCookie({ ...session, internal: false, origin_sub: session.sub, auth_method: 'email_code' })
  await linkModeratorGrants({ id: session.sub, email: session.email, name: session.name })
  await logSecurityEvent({ event: 'login_success', username: email, userId: session.sub, ip, userAgent, detail: { via: 'email_code', role: session.role } })
  const pages = await surfacesForEmail(email)
  const next = pages.length === 1 ? (await import('@/lib/domain/moderator-access')).surfaceByKey(pages[0])?.path ?? '/dashboard' : '/dashboard'
  return NextResponse.json({ ok: true, next, historical: { connected: historical.connected, roles: historical.roles } })
}
