import { NextRequest, NextResponse } from 'next/server'
import { z } from 'zod'
import { getSessionUser } from '@/lib/auth'
import { createAdminClient } from '@/lib/supabase/admin'
import { getCompanyContext } from '@/lib/data/companies'
import {
  approveDispatcher, bulkInviteMembers, inviteOrHold, loadCarrierCompanyInfo, reinstateMember, rejectDispatcher, remindCompany, revokeDispatcher, setDispatcherPermission,
  setMemberRoleType, updateCarrierProfile, updateOfficialInfo,
} from '@/lib/data/carrier-company'
import { REJECTION_REASONS, canManageCarrierCompany } from '@/lib/domain/carrier-company'
import { CARRIER_COMPANY_ROLES, type RoleType } from '@/lib/domain/modes'
import { BULK_INVITE_LIMIT, parseEmailList } from '@/lib/domain/company-invites'
import type { Company, CompanyMembership } from '@/types/db'

/**
 * Carrier Dashboard → Company Info (2026-09-19).
 *
 *   GET  ?company=<id>   read-only Company Info for the signed-in dispatcher
 *                        (HeavyHaul admins pick any carrier with ?company=)
 *   POST { op, … }       management — every op re-checks on the server (§25):
 *                        signed in · dispatcher or HeavyHaul admin · member of
 *                        THIS company · relationship verified · permission
 *                        company_admin (or HeavyHaul admin) · not revoked ·
 *                        company id matches. Hiding a button is never the gate.
 *
 * ops: approve · reject · revoke · permission · profile · official (HeavyHaul
 * admin only) · remind (the pending dispatcher's own "resend") · invite
 * (name + email → verified on the spot, or held until they sign up) ·
 * bulk_invite (pasted list, one send) · member_role (D15: the member's title —
 * Dispatcher, Safety Manager, Permit Manager, Accounting, Fleet Manager, Carrier Driver)
 */

const reasonEnum = z.enum(REJECTION_REASONS.map((r) => r.value) as [string, ...string[]])
const sourceEnum = z.enum(['FMCSA', 'HIGHWAY', 'MANUAL_ADMIN', 'OTHER_VERIFIED_SOURCE'])
/** D15: what a carrier may hand out. Default (omitted) is Carrier Dispatcher. */
const roleTypeEnum = z.enum(CARRIER_COMPANY_ROLES as [RoleType, ...RoleType[]])

const schema = z.discriminatedUnion('op', [
  z.object({ op: z.literal('approve'), company_id: z.string().uuid(), membership_id: z.string().uuid(), role_type: roleTypeEnum.optional() }),
  z.object({ op: z.literal('member_role'), company_id: z.string().uuid(), membership_id: z.string().uuid(), role_type: roleTypeEnum }),
  z.object({ op: z.literal('reject'), company_id: z.string().uuid(), membership_id: z.string().uuid(), reason: reasonEnum, note: z.string().trim().max(1000).optional() }),
  z.object({ op: z.literal('revoke'), company_id: z.string().uuid(), membership_id: z.string().uuid() }),
  z.object({ op: z.literal('reinstate'), company_id: z.string().uuid(), membership_id: z.string().uuid() }),
  z.object({ op: z.literal('permission'), company_id: z.string().uuid(), membership_id: z.string().uuid(), level: z.enum(['member', 'company_admin']) }),
  z.object({
    op: z.literal('profile'),
    company_id: z.string().uuid(),
    display_name: z.string().trim().min(1).max(160).optional(),
    operations_phone: z.string().trim().max(40).optional(),
    operations_email: z.string().trim().max(200).optional(),
    dispatch_contact: z.string().trim().max(200).optional(),
    preferred_contact: z.enum(['', 'email', 'phone', 'operations_email', 'operations_phone']).optional(),
    description: z.string().trim().max(2000).optional(),
  }),
  z.object({
    op: z.literal('official'),
    company_id: z.string().uuid(),
    official_company_email: z.string().trim().max(200).optional(),
    official_email_status: z.enum(['unverified', 'pending', 'verified', 'rejected']).optional(),
    official_email_verification_source: sourceEnum.nullable().optional(),
    verification_source: sourceEnum.nullable().optional(),
  }),
  z.object({ op: z.literal('remind') }),
  z.object({ op: z.literal('invite'), company_id: z.string().uuid(), name: z.string().trim().min(1).max(120), email: z.string().trim().email().max(200), make_admin: z.boolean().optional(), role_type: roleTypeEnum.optional() }),
  z.object({ op: z.literal('bulk_invite'), company_id: z.string().uuid(), emails: z.string().min(1).max(50_000), make_admin: z.boolean().optional(), role_type: roleTypeEnum.optional() }),
])

export async function GET(req: NextRequest) {
  const user = await getSessionUser()
  if (!user) return NextResponse.json({ error: 'Sign in first.' }, { status: 401 })
  if (user.role !== 'dispatcher' && user.role !== 'admin') {
    return NextResponse.json({ error: 'Company Info is part of the Carrier Dashboard.' }, { status: 403 })
  }
  const info = await loadCarrierCompanyInfo(user, { companyId: req.nextUrl.searchParams.get('company') })
  return NextResponse.json(info)
}

export async function POST(req: NextRequest) {
  const user = await getSessionUser()
  if (!user) return NextResponse.json({ error: 'Sign in first.' }, { status: 401 })
  if (user.role !== 'dispatcher' && user.role !== 'admin') {
    return NextResponse.json({ error: 'Company Info is part of the Carrier Dashboard.' }, { status: 403 })
  }
  const parsed = schema.safeParse(await req.json().catch(() => ({})))
  if (!parsed.success) return NextResponse.json({ error: 'Invalid request.' }, { status: 400 })
  const input = parsed.data
  const isPlatformAdmin = user.role === 'admin'
  const actor = {
    id: user.id,
    label: user.name || user.email,
    email: user.email,
    isPlatformAdmin,
    ip: req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ?? null,
  }

  // The caller's own relationship (dispatchers) — the only thing a management op may lean on.
  const ctx = isPlatformAdmin ? null : await getCompanyContext(user.id)
  if (ctx && !ctx.available) return NextResponse.json({ error: 'Company Info needs database migration 0013.' }, { status: 503 })

  if (input.op === 'remind') {
    if (!ctx?.company || !ctx.membership) return NextResponse.json({ error: 'No carrier is connected to your account.' }, { status: 400 })
    const r = await remindCompany({ company: ctx.company, membership: ctx.membership, actor })
    return r.ok ? NextResponse.json(r) : NextResponse.json({ error: r.error }, { status: 400 })
  }

  const admin = createAdminClient()
  const { data: companyRow } = await admin.from('companies').select('*').eq('id', input.company_id).eq('company_type', 'carrier').maybeSingle()
  const company = (companyRow ?? null) as Company | null
  if (!company) return NextResponse.json({ error: 'Carrier not found.' }, { status: 404 })
  const membership = (ctx?.membership ?? null) as CompanyMembership | null
  if (!canManageCarrierCompany({ membership, companyId: company.id, viewerIsPlatformAdmin: isPlatformAdmin })) {
    return NextResponse.json({ error: 'Only a verified Company Admin of this carrier can do this.' }, { status: 403 })
  }
  if (company.locked && !isPlatformAdmin) {
    return NextResponse.json({ error: 'This company profile is locked by HeavyHaul Agent.' }, { status: 423 })
  }

  switch (input.op) {
    case 'approve': {
      const r = await approveDispatcher({ company, membershipId: input.membership_id, actor, roleType: input.role_type ?? 'carrier_dispatcher' })
      return r.ok ? NextResponse.json({ ok: true }) : NextResponse.json({ error: r.error }, { status: 400 })
    }
    case 'member_role': {
      const r = await setMemberRoleType({ company, membershipId: input.membership_id, roleType: input.role_type, actor })
      return r.ok ? NextResponse.json({ ok: true, changed: r.changed }) : NextResponse.json({ error: r.error }, { status: 400 })
    }
    case 'reject': {
      const reasonLabel = REJECTION_REASONS.find((x) => x.value === input.reason)?.label ?? input.reason
      const r = await rejectDispatcher({ company, membershipId: input.membership_id, reason: input.reason as (typeof REJECTION_REASONS)[number]['value'], note: input.note || null, reasonLabel, actor })
      return r.ok ? NextResponse.json({ ok: true }) : NextResponse.json({ error: r.error }, { status: 400 })
    }
    case 'revoke': {
      const r = await revokeDispatcher({ company, membershipId: input.membership_id, actor, actorMembership: membership })
      return r.ok ? NextResponse.json({ ok: true }) : NextResponse.json({ error: r.error }, { status: 400 })
    }
    case 'reinstate': {
      const r = await reinstateMember({ company, membershipId: input.membership_id, actor })
      return r.ok ? NextResponse.json({ ok: true }) : NextResponse.json({ error: r.error }, { status: 400 })
    }
    case 'permission': {
      const r = await setDispatcherPermission({ company, membershipId: input.membership_id, level: input.level, actor })
      return r.ok ? NextResponse.json({ ok: true }) : NextResponse.json({ error: r.error }, { status: 400 })
    }
    case 'profile': {
      const { op: _op, company_id: _c, ...patch } = input
      void _op; void _c
      const r = await updateCarrierProfile({ company, patch, actor })
      return r.ok ? NextResponse.json({ ok: true, changed: r.changed }) : NextResponse.json({ error: r.error }, { status: 400 })
    }
    case 'invite': {
      const r = await inviteOrHold({ company, name: input.name, email: input.email, makeAdmin: !!input.make_admin, actor, roleType: input.role_type ?? 'carrier_dispatcher' })
      return r.ok ? NextResponse.json(r) : NextResponse.json({ error: r.error }, { status: 400 })
    }
    case 'bulk_invite': {
      const { emails, invalid } = parseEmailList(input.emails)
      if (emails.length === 0) return NextResponse.json({ error: 'No valid email addresses found in the pasted list.' }, { status: 400 })
      if (emails.length > BULK_INVITE_LIMIT) return NextResponse.json({ error: `Up to ${BULK_INVITE_LIMIT} addresses per batch — split the list.` }, { status: 400 })
      const r = await bulkInviteMembers({ company, emails, makeAdmin: !!input.make_admin, actor, roleType: input.role_type ?? 'carrier_dispatcher' })
      return NextResponse.json({ ok: true, results: r.results, invalid })
    }
    case 'official': {
      if (!isPlatformAdmin) return NextResponse.json({ error: 'Official company data is changed by a verification source or HeavyHaul Agent.' }, { status: 403 })
      const { op: _op, company_id: _c, ...patch } = input
      void _op; void _c
      const r = await updateOfficialInfo({ company, patch, actor })
      return r.ok ? NextResponse.json({ ok: true }) : NextResponse.json({ error: r.error }, { status: 400 })
    }
  }
}
