import { NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { verifyJoinToken } from '@/lib/email/tokens'
import { createAdminClient } from '@/lib/supabase/admin'
import { ensureSelfAccount } from '@/lib/auth/self-accounts'
import { findEnvUserById } from '@/lib/auth/env-users'
import { effectiveRole, getAccountOverride } from '@/lib/auth/accounts'
import { setSessionCookie } from '@/lib/auth/session'
import { issueEmailCode, verifyEmailCode } from '@/lib/security/verification-codes'
import { clientIp, rateLimit } from '@/lib/security/rate-limit'
import { logSecurityEvent } from '@/lib/security/events'
import { publicOrigin } from '@/lib/app-url'
import { emailPattern } from '@/lib/like'
import { logLeadEvent, markLeadSignedUp } from '@/lib/data/broker-lead-hooks'
import { carrierLeadRole } from '@/lib/domain/broker-leads'

/**
 * The join link in a lead campaign email (2026-09-23): the lead's email and
 * brokerage are already known, so sign-up is one emailed code. The account is
 * a freight broker; the pre-verified relation is waiting on their dashboard
 * to be confirmed.
 *
 *   POST { op: 'start',  token }                    → code emailed to the lead's address
 *   POST { op: 'verify', token, code, name?, phone? } → account + session, → /fb-dashboard
 */
const schema = z.discriminatedUnion('op', [
  z.object({ op: z.literal('start'), token: z.string().min(10).max(2000) }),
  z.object({ op: z.literal('verify'), token: z.string().min(10).max(2000), code: z.string().trim().min(6).max(8), name: z.string().trim().max(120).optional(), phone: z.string().trim().max(40).optional() }),
])

export async function POST(req: NextRequest) {
  const parsed = schema.safeParse(await req.json().catch(() => ({})))
  if (!parsed.success) return NextResponse.json({ error: 'Check the details and try again.' }, { status: 400 })
  const input = parsed.data
  const ip = clientIp(req.headers)
  const userAgent = req.headers.get('user-agent')
  const t = await verifyJoinToken(input.token)
  if (!t) return NextResponse.json({ error: 'This link is no longer valid. Ask for a new invitation.' }, { status: 400 })
  const admin = createAdminClient()
  const q1 = await admin.from('broker_agent_leads').select('id, email, email_normalized, first_name, last_name, broker_company_name, phone, lead_kind, lead_type').eq('id', t.leadId).maybeSingle()
  const lead = q1.error ? (await admin.from('broker_agent_leads').select('id, email, email_normalized, first_name, last_name, broker_company_name, phone').eq('id', t.leadId).maybeSingle()).data : q1.data
  const L = lead as { id: string; email: string; email_normalized: string; first_name: string | null; last_name: string | null; broker_company_name: string | null; phone: string | null; lead_kind?: string | null; lead_type?: string | null } | null
  // A carrier lead becomes a dispatcher or driver account; a broker lead a freight broker (2026-09-25).
  const carrier = L?.lead_kind === 'carrier'
  const carrierAccount = carrier ? carrierLeadRole(L?.lead_type) : null
  if (!L || L.email_normalized !== t.email) return NextResponse.json({ error: 'This link does not match a current invitation.' }, { status: 400 })
  const email = L.email_normalized

  if (input.op === 'start') {
    const gate = rateLimit(`join:${ip}`, 10, 15 * 60_000)
    if (!gate.allowed) return NextResponse.json({ error: 'Too many requests. Try again in a few minutes.' }, { status: 429 })
    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 })
    await logLeadEvent({ leadId: L.id, type: 'join_link_opened', detail: { code_status: r.status } })
    return NextResponse.json({ ok: true, status: r.status, ...(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 name = input.name?.trim() || [L.first_name, L.last_name].filter(Boolean).join(' ') || email
  // An admin-provisioned login with this email signs in as itself; otherwise a self-service freight broker account.
  const { data: envProfile } = await admin.from('profiles').select('id').ilike('email', emailPattern(email)).maybeSingle()
  const envUser = envProfile ? findEnvUserById((envProfile as { id: string }).id) : null
  let session: { sub: string; email: string; name: string; role: 'broker' | 'dispatcher' | 'driver' | 'admin'; company: string | null }
  let created = false
  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 }
  }
  else {
    const r = await ensureSelfAccount({ email, name, phone: input.phone || L.phone, role: carrierAccount ? carrierAccount.role : 'broker' })
    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 })
    if (r.account.role === 'admin') return NextResponse.json({ error: 'Admin accounts must use password sign-in.' }, { status: 403 })
    session = { sub: r.account.id, email: r.account.email, name: r.account.name, role: r.account.role, company: null }
    created = r.created
  }
  await setSessionCookie({ ...session, internal: false, origin_sub: session.sub, auth_method: 'email_code' })
  await markLeadSignedUp({ leadId: L.id, email, userId: session.sub, via: 'join_link' })
  await logSecurityEvent({ event: 'login_success', username: email, userId: session.sub, ip, userAgent, detail: { via: carrier ? 'carrier_join_link' : 'broker_join_link', created, role: session.role, lead_id: L.id } })
  const next = carrierAccount ? (session.role === 'admin' ? '/dashboard' : carrierAccount.next) : session.role === 'broker' ? '/fb-dashboard?tab=company-info' : '/dashboard'
  return NextResponse.json({ ok: true, created, role: session.role, next })
}
