import 'server-only'

import { STATE_NAMES } from '@/lib/domain/states'

/**
 * Adapter to the EXISTING Flask/MongoDB backend (permit extraction, AI chat,
 * regulations, voice, Synchron/TMS integrations). We reuse that code through
 * this HTTP boundary instead of rewriting it.
 *
 * Contract: docs/FLASK_ADAPTER.md. Configure via env:
 *   FLASK_API_BASE_URL  e.g. https://api.internal.example.com
 *   FLASK_API_KEY       bearer token (optional, if the Flask app requires it)
 *
 * Every method degrades gracefully when the backend is unreachable or not yet
 * configured: callers receive { ok: false, error } and the UI shows an honest
 * "processing pending" state instead of fake data.
 */

export interface AdapterResult<T> {
  ok: boolean
  data?: T
  error?: string
}

export interface PermitExtraction {
  state_code?: string
  permit_number?: string
  effective_date?: string // YYYY-MM-DD
  expiration_date?: string // YYYY-MM-DD
  length_in?: number
  width_in?: number
  height_in?: number
  weight_lbs?: number
  route_description?: string
  route_token?: string
  route_source?: 'synchron' | 'workspace'
  route_status?: 'ready' | 'needs_preparation' | 'failed' | 'unsupported'
  route_links?: string[]
  route_gpx_url?: string
  route_hummer_url?: string
  restrictions?: string[]
  curfews?: string[]
  escorts?: string[]
  /** The state's fee when the extractor names it outright (migration 0033). */
  permit_fee?: number | string
  permit_cost?: number | string
  fee_amount?: number | string
  currency?: string
  /** Full text read off the permit — the fee is read from here when no field carries it. */
  text?: string
  raw?: Record<string, unknown>
}

export interface PermitProfileFeedPage {
  items: Array<{
    source_system: string
    source_key: string
    synchron_order_token?: string | null
    synchron_permit_token?: string | null
    observation: Record<string, unknown>
  }>
  next_cursor: string | null
  has_more: boolean
}

/** Bounded, read-only Mongo observations for Workspace profile backfill. */
export function getPermitProfileFeed(feed: 'orders' | 'permit_extractions', after?: string | null) {
  const query = new URLSearchParams({ feed, limit: '20' })
  if (after) query.set('after', after)
  return callFlask<PermitProfileFeedPage>(`/api/workspace/permit-profile-observations?${query}`, { timeoutMs: 60_000, cache: 'no-store' })
}

export interface RateConExtraction {
  origin?: string
  destination?: string
  commodity?: string
  carrier_name?: string
  broker_name?: string
  pickup_date?: string
  delivery_date?: string
  length_in?: number
  width_in?: number
  height_in?: number
  weight_lbs?: number
  raw?: Record<string, unknown>
}

export interface ExtractionContext {
  tripId?: string
  documentId?: string
  permitId?: string
  /** Stable canonical S3 URL saved with the GPT extraction record. */
  sourceUrl?: string
}

export interface AiAnswer {
  answer: string
  confidence?: 'high' | 'partial' | 'low'
  sources?: string[]
}

export interface AiHistoryTurn {
  user: string
  assistant: string
}

export interface VoiceSession {
  token: string
  url: string
  room_name: string
  session_id: string
  language: string
}

export function isFlaskConfigured(): boolean {
  return Boolean(process.env.HHA_API_BASE_URL || process.env.FLASK_API_BASE_URL)
}

async function callFlask<T>(
  path: string,
  init: RequestInit & { timeoutMs?: number } = {},
): Promise<AdapterResult<T>> {
  const base = process.env.HHA_API_BASE_URL || process.env.FLASK_API_BASE_URL
  if (!base) {
    return { ok: false, error: 'HHA_API_BASE_URL is not configured' }
  }
  const { timeoutMs = 60_000, ...rest } = init
  const controller = new AbortController()
  const timer = setTimeout(() => controller.abort(), timeoutMs)
  try {
    const res = await fetch(`${base.replace(/\/$/, '')}${path}`, {
      ...rest,
      signal: controller.signal,
      headers: {
        ...(rest.body instanceof FormData ? {} : { 'Content-Type': 'application/json' }),
        ...((process.env.HHA_API_CLIENT_SECRET || process.env.HHA_WORKSPACE_SYNC_TOKEN || process.env.FLASK_API_KEY)
          ? { Authorization: `Bearer ${process.env.HHA_API_CLIENT_SECRET || process.env.HHA_WORKSPACE_SYNC_TOKEN || process.env.FLASK_API_KEY}` }
          : {}),
        ...(process.env.HHA_API_CLIENT_ID
          ? { 'X-HHA-Client-ID': process.env.HHA_API_CLIENT_ID }
          : {}),
        ...(rest.headers ?? {}),
      },
    })
    if (!res.ok) {
      return { ok: false, error: `Backend responded ${res.status}` }
    }
    return { ok: true, data: (await res.json()) as T }
  } catch (err) {
    return { ok: false, error: err instanceof Error ? err.message : 'Backend unreachable' }
  } finally {
    clearTimeout(timer)
  }
}

/** Extract structured data from a permit PDF/image. */
export async function extractPermit(
  file: Blob,
  fileName: string,
  context: ExtractionContext = {},
): Promise<AdapterResult<PermitExtraction>> {
  const form = new FormData()
  form.append('file', file, fileName)
  if (context.tripId) form.append('trip_id', context.tripId)
  if (context.documentId) form.append('document_id', context.documentId)
  if (context.permitId) form.append('permit_id', context.permitId)
  if (context.sourceUrl) form.append('source_url', context.sourceUrl)
  const result = await callFlask<Record<string, unknown>>('/api/workspace/extract/permit', {
    method: 'POST',
    body: form,
    timeoutMs: 300_000,
  })
  if (!result.ok || !result.data) return { ok: false, error: result.error }
  const rawResult = context.sourceUrl
    ? { ...result.data, source_url: context.sourceUrl, permit_url: context.sourceUrl }
    : result.data

  const states = Array.isArray(rawResult.states_info) ? rawResult.states_info : []
  const state = asRecord(states[0])
  const order = asRecord(rawResult.order_info)
  const dimensions = asRecord(order.overall_dimensions)
  const permitResponses = Array.isArray(rawResult.permit_responses)
    ? rawResult.permit_responses
    : []
  const llm = asRecord(asRecord(permitResponses[0]).llm_response)
  const stateValue = String(state.state_name || asRecord(permitResponses[0]).state_name || '')
  const routeTexts = asRecord(state.route_texts)
  const routeDescription = Array.isArray(state.route_texts)
    ? state.route_texts.filter((text): text is string => typeof text === 'string').join(' ')
    : Object.keys(routeTexts).length
      ? [routeTexts.routing_from, routeTexts.routing_to, routeTexts.via]
        .filter((text): text is string => typeof text === 'string' && Boolean(text.trim())).join(' ')
      : stringValue(state.route_texts)
  const routeLinks = Array.isArray(state.route_links)
    ? state.route_links.filter((link): link is string => typeof link === 'string' && isSecureUrl(link))
    : []
  const routeToken = stringValue(state.route_token)

  return {
    ok: true,
    data: {
      state_code: stateCode(stateValue),
      permit_number: stringValue(state.permit_number || llm.permit_number),
      effective_date: stringValue(state.effective_date || llm.permit_effective_date),
      expiration_date: stringValue(state.expiration_date || llm.permit_expiration_date),
      length_in: dimensionInches(dimensions.overall_length),
      width_in: dimensionInches(dimensions.overall_width),
      height_in: dimensionInches(dimensions.overall_height),
      weight_lbs: numberValue(dimensions.overall_weight),
      route_description: routeDescription,
      route_token: routeToken,
      route_source: state.route_source === 'synchron' || state.route_source === 'workspace'
        ? state.route_source : undefined,
      // A matched token is ready only when GPT also returns a usable map link.
      route_status: state.route_status === 'unsupported' || state.route_status === 'failed'
        ? state.route_status : routeToken
          ? state.route_status === 'ready' && routeLinks.length ? 'ready' : 'needs_preparation'
          : 'failed',
      route_links: routeLinks,
      restrictions: stringList(llm.restrictions || llm.travel_restrictions),
      curfews: stringList(llm.curfews || llm.curfew_restrictions),
      escorts: stringList(llm.escorts || llm.escort_requirements),
      raw: rawResult,
    },
  }
}

/** Extract structured data from a rate confirmation. */
export async function extractRateConfirmation(
  file: Blob,
  fileName: string,
  context: ExtractionContext = {},
): Promise<AdapterResult<RateConExtraction>> {
  const form = new FormData()
  form.append('file', file, fileName)
  if (context.tripId) form.append('trip_id', context.tripId)
  if (context.documentId) form.append('document_id', context.documentId)
  if (context.sourceUrl) form.append('source_url', context.sourceUrl)
  const result = await callFlask<Record<string, unknown>>(
    '/api/workspace/extract/rate-confirmation',
    {
      method: 'POST',
      body: form,
      timeoutMs: 300_000,
    },
  )
  if (!result.ok || !result.data) return { ok: false, error: result.error }
  const rawResult = context.sourceUrl
    ? { ...result.data, source_url: context.sourceUrl, url: context.sourceUrl }
    : result.data
  const info = asRecord(rawResult.ext_info)
  const carrier = asRecord(info.carrier_info)
  const commodity = asRecord(info.commodity_details || info.commodity_info)
  const load = asRecord(info.load_dimensions || info.dimensions)
  return {
    ok: true,
    data: {
      origin: stringValue(info.pickup_address || info.origin),
      destination: stringValue(info.delivery_address || info.destination),
      commodity: stringValue(
        commodity.commodity_description || commodity.description || info.commodity,
      ),
      carrier_name: stringValue(carrier.company_name || carrier.name || info.carrier_name),
      broker_name: stringValue(info.broker_name || asRecord(info.broker_info).company_name),
      pickup_date: stringValue(info.pickup_date || commodity.pickup_date),
      delivery_date: stringValue(info.delivery_date || commodity.delivery_date),
      length_in: dimensionInches(commodity.length || load.length),
      width_in: dimensionInches(commodity.width || load.width),
      height_in: dimensionInches(commodity.height || load.height),
      weight_lbs: numberValue(commodity.weight || load.weight || info.weight),
      raw: rawResult,
    },
  }
}

function asRecord(value: unknown): Record<string, unknown> {
  return value && typeof value === 'object' && !Array.isArray(value)
    ? (value as Record<string, unknown>)
    : {}
}

function stringValue(value: unknown): string | undefined {
  if (value === null || value === undefined || value === '') return undefined
  return String(value)
}

function numberValue(value: unknown): number | undefined {
  if (typeof value === 'number' && Number.isFinite(value)) return value
  const parsed = Number(String(value ?? '').replace(/[^\d.-]/g, ''))
  return Number.isFinite(parsed) ? parsed : undefined
}

function dimensionInches(value: unknown): number | undefined {
  if (value && typeof value === 'object') {
    const dimension = asRecord(value)
    const feet = numberValue(dimension.feet) ?? 0
    const inches = numberValue(dimension.inches ?? dimension.inch) ?? 0
    return feet * 12 + inches
  }
  const text = String(value ?? '').trim().toLowerCase()
  if (!text) return undefined
  const feetMatch = text.match(/([\d.]+)\s*(?:ft|feet|')/)
  const inchMatch = text.match(/([\d.]+)\s*(?:in|inches|")/)
  if (feetMatch || inchMatch) {
    return (Number(feetMatch?.[1] ?? 0) * 12) + Number(inchMatch?.[1] ?? 0)
  }
  const numeric = numberValue(text)
  return numeric === undefined ? undefined : numeric * 12
}

function stringList(value: unknown): string[] | undefined {
  if (Array.isArray(value)) return value.map(String).filter(Boolean)
  if (typeof value === 'string' && value.trim()) return [value.trim()]
  return undefined
}

function isSecureUrl(value: string): boolean {
  try {
    const url = new URL(value)
    return url.protocol === 'https:' && Boolean(url.hostname) && !url.username && !url.password
  } catch {
    return false
  }
}

function stateCode(value: string): string | undefined {
  const normalized = value.trim()
  if (!normalized) return undefined
  if (/^[a-z]{2}$/i.test(normalized)) return normalized.toUpperCase()
  const match = Object.entries(STATE_NAMES).find(
    ([, name]) => name.toLowerCase() === normalized.toLowerCase(),
  )
  return match?.[0] || normalized.slice(0, 2).toUpperCase()
}

/**
 * Ask the trip-context AI a question (permit / provisions / curfew / escort /
 * validity / dimensions). The GPT backend resolves the immutable Workspace
 * trip UUID against MongoDB all_trips and assembles the agent context there.
 */
export async function askTripAi(payload: {
  question: string
  workspace_trip_id: string
  history?: AiHistoryTurn[]
  language?: string
  request_id?: string
  /** Who is asking, and whether they may hear state permit fees (2026-09-24, §15). */
  viewer_role?: string | null
  permit_cost_visible?: boolean
}): Promise<AdapterResult<AiAnswer>> {
  const result = await callFlask<{
    response: string
    confidence?: unknown
    source_citations?: unknown[]
    sources_used?: unknown[]
  }>('/api/workspace/logistics-chat', {
    method: 'POST',
    headers: payload.request_id ? { 'X-Trip-AI-Request-ID': payload.request_id } : undefined,
    body: JSON.stringify({
      message: payload.question,
      viewer_role: payload.viewer_role ?? null,
      // The backend must not answer a pilot with permit fees; the route also
      // redacts any that slip through.
      permit_cost_visible: payload.permit_cost_visible !== false,
      // This dedicated API accepts the immutable Supabase UUID directly. It
      // does not use the Synchron order-token contract.
      workspace_trip_id: payload.workspace_trip_id,
      language: payload.language || 'en',
      browser_id: `workspace-${payload.workspace_trip_id}`,
      history: (payload.history ?? []).slice(-2),
    }),
    timeoutMs: 90_000,
  })
  if (!result.ok || !result.data) return { ok: false, error: result.error }
  const confidence = normalizeAgentConfidence(result.data.confidence)
  const sources = normalizeAgentSources(
    result.data.source_citations?.length
      ? result.data.source_citations
      : result.data.sources_used,
  )
  return {
    ok: true,
    data: {
      answer: result.data.response,
      confidence,
      sources,
    },
  }
}

export async function createTripVoiceSession(payload: {
  workspace_trip_id: string
  browser_id: string
  name: string
  language: string
  request_id?: string
}): Promise<AdapterResult<VoiceSession>> {
  return callFlask<VoiceSession>('/api/workspace/logistics-voice/token', {
    method: 'POST',
    headers: payload.request_id ? { 'X-Trip-AI-Request-ID': payload.request_id } : undefined,
    body: JSON.stringify({
      workspace_trip_id: payload.workspace_trip_id,
      browser_id: payload.browser_id,
      name: payload.name,
      language: payload.language,
    }),
    timeoutMs: 30_000,
  })
}

export function normalizeAgentConfidence(value: unknown): AiAnswer['confidence'] | undefined {
  if (value === 'high' || value === 'partial' || value === 'low') return value
  const numeric = typeof value === 'number' ? value : Number(value)
  if (!Number.isFinite(numeric)) return undefined
  if (numeric >= 80) return 'high'
  if (numeric >= 50) return 'partial'
  return 'low'
}

export function normalizeAgentSources(values: unknown[] | undefined): string[] {
  if (!Array.isArray(values)) return []
  const labels = values
    .map((value) => {
      if (typeof value === 'string') return value.trim()
      if (!value || typeof value !== 'object') return ''
      const citation = value as Record<string, unknown>
      return String(citation.label || citation.source || citation.id || '').trim()
    })
    .filter(Boolean)
  return [...new Set(labels)]
}

/** Legacy contract kept for existing consumers; no Workspace request path calls it. */
export interface SynchronTripData {
  commodity: string
  load: { length_in: number | null; width_in: number | null; height_in: number | null; weight_lbs: number | null }
  overall: { length_in: number | null; width_in: number | null; height_in: number | null; weight_lbs: number | null }
  unit: {
    axle_count: number | null
    axle_spacings_in: number[] | null
    kingpin_to_rear_axle_in: number | null
    axle_weights_lbs: number[] | null
    truck: { unit_number: string | null; vin: string | null; make: string | null; model: string | null; year: number | null }
    trailer: { unit_number: string | null; vin: string | null; make: string | null; model: string | null; year: number | null; axle_count: number | null }
  } | null
  replaces_permit: { state_code: string; permit_number: string | null; expiration_date: string | null } | null
}

/** Legacy API placeholder; GPT has no matching route. */
export async function createSynchronOrder(payload: {
  type: 'permit' | 'route'
  trip_ref: string
  state_code?: string | null
  carrier_name: string
  contact_email: string
  contact_name?: string
  payer?: 'requester' | 'broker' | 'carrier' | null
  notes?: string | null
  document_urls?: string[]
  trip_data?: SynchronTripData
}): Promise<AdapterResult<{ order_id?: string }>> {
  return callFlask<{ order_id?: string }>('/api/synchron/orders', {
    method: 'POST',
    body: JSON.stringify(payload),
  })
}
