import { NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { getSessionUser } from '@/lib/auth'
import { createAdminClient } from '@/lib/supabase/admin'
import { emailPattern } from '@/lib/like'
import type { CarrierContact } from '@/types/db'

/**
 * Saved contacts (Task 98, 2026-09-09) — add, edit and remove the drivers and
 * brokers a dispatcher works with, so creating a trip becomes a pick instead
 * of four fields retyped (Task 99).
 *
 * ops:
 *   add     save a person; `invite` also records an account invitation
 *   update  correct their details
 *   remove  drop them from the address book
 *
 * A contact is private to its owner: every op is scoped by `owner_id`, so one
 * carrier can never read or change another's list.
 *
 * Email SENDING is the email backend (later) — the same standing boundary as
 * trip invites, company approvals and Synchron orders. `invited_at` records
 * that the invitation was raised; nothing here claims mail was delivered.
 */

const NEEDS_MIGRATION = 'Saved contacts need database migration 0014.'

const person = {
  name: z.string().trim().min(1).max(120),
  email: z.string().trim().email().max(200),
  // Same rule as the trip invite (Task 33): phone required, extension optional.
  phone: z.string().trim().min(5).max(40),
  phone_ext: z.string().trim().max(20).optional().or(z.literal('')),
  role: z.enum(['driver', 'broker', 'dispatcher']),
}

const schema = z.discriminatedUnion('op', [
  z.object({ op: z.literal('add'), invite: z.boolean().optional().default(false), ...person }),
  z.object({ op: z.literal('update'), id: z.string().uuid(), ...person }),
  z.object({ op: z.literal('remove'), id: z.string().uuid() }),
])

function tableMissing(error: { message?: string; code?: string } | null | undefined): boolean {
  if (!error) return false
  return (
    error.code === '42P01' ||
    error.code === 'PGRST205' ||
    /does not exist|schema cache|could not find the table/i.test(error.message ?? '')
  )
}

export async function POST(req: NextRequest) {
  const user = await getSessionUser()
  if (!user) return NextResponse.json({ error: 'Sign in first.' }, { status: 401 })

  const parsed = schema.safeParse(await req.json().catch(() => ({})))
  if (!parsed.success) {
    return NextResponse.json(
      { error: 'A name, a valid email and a phone number are required.' },
      { status: 400 },
    )
  }
  const input = parsed.data
  const admin = createAdminClient()

  if (input.op === 'remove') {
    const { error } = await admin
      .from('carrier_contacts')
      .delete()
      .eq('id', input.id)
      .eq('owner_id', user.id)
    if (tableMissing(error)) return NextResponse.json({ error: NEEDS_MIGRATION }, { status: 503 })
    if (error) return NextResponse.json({ error: 'Could not remove the contact.' }, { status: 500 })
    return NextResponse.json({ ok: true })
  }

  const email = input.email.trim().toLowerCase()
  const row = {
    name: input.name,
    email,
    phone: input.phone,
    phone_ext: input.phone_ext || null,
    role: input.role,
    updated_at: new Date().toISOString(),
  }

  if (input.op === 'update') {
    const { data, error } = await admin
      .from('carrier_contacts')
      .update(row)
      .eq('id', input.id)
      .eq('owner_id', user.id)
      .select()
      .maybeSingle()
    if (tableMissing(error)) return NextResponse.json({ error: NEEDS_MIGRATION }, { status: 503 })
    if (error) {
      const duplicate = error.message.includes('duplicate')
      return NextResponse.json(
        { error: duplicate ? 'That email is already saved for this role.' : 'Could not save the contact.' },
        { status: duplicate ? 409 : 500 },
      )
    }
    if (!data) return NextResponse.json({ error: 'Contact not found.' }, { status: 404 })
    return NextResponse.json({ ok: true, contact: data as CarrierContact })
  }

  // ---- add ----
  // Link the person to their account when they already have one, so the list
  // can show who has joined. Matching by email is what invitations already do.
  const { data: existing } = await admin
    .from('profiles')
    .select('id')
    .ilike('email', emailPattern(email))
    .maybeSingle()

  const { data, error } = await admin
    .from('carrier_contacts')
    .insert({
      ...row,
      owner_id: user.id,
      user_id: existing?.id ?? null,
      // Nash: adding a driver "kind of triggers an invite email to him to
      // create an account and to join". The invitation is recorded now;
      // delivery waits for the email backend.
      invited_at: input.invite && !existing ? new Date().toISOString() : null,
    })
    .select()
    .single()
  if (tableMissing(error)) return NextResponse.json({ error: NEEDS_MIGRATION }, { status: 503 })
  if (error) {
    const duplicate = error.message.includes('duplicate')
    return NextResponse.json(
      { error: duplicate ? 'That person is already in your contacts.' : 'Could not save the contact.' },
      { status: duplicate ? 409 : 500 },
    )
  }
  return NextResponse.json({
    ok: true,
    contact: data as CarrierContact,
    /** True when they already have an account — nothing to invite. */
    already_a_user: !!existing,
  })
}
