import 'server-only'
import { createAdminClient } from '@/lib/supabase/admin'
import { getMyParticipant, type SessionUser } from '@/lib/auth'
import { adminParticipantFor } from '@/lib/domain/participants'
import { canAccessTrip, getVisibleTrips, getTripCompletions } from '@/lib/data/trips'
import { loadDriverRouteRequests, loadDriverUnits, signRouteFormats } from '@/lib/data/driver'
import { resolveDocumentUrls } from '@/lib/data/document-urls'
import { loadVisibleChat } from '@/lib/data/chat'
import { getRouteCreditBalance } from '@/lib/data/credits'
import { loadContactPicker, loadInviteContacts } from '@/lib/data/contacts'
import { loadTripCarrier } from '@/lib/data/carrier-assignment'
import { enrichParticipantProvenance } from '@/lib/data/participant-provenance'
import { pilotAccessFromEvents } from '@/lib/domain/pilot'
import { stripPermitCostFor } from '@/lib/domain/permit-cost'
import { activeDimensionWarnings } from '@/lib/domain/dimension-alerts'
import type {
  Permit,
  ServiceRequest,
  Trip,
  TripDocument,
  TripEvent,
  TripParticipant,
  TripWarning,
} from '@/types/db'

export type { WorkspaceParticipant } from '@/lib/data/participant-provenance'

/**
 * Trip-view data loaders, one per role page (article "Role-Specific Page
 * Separation", 2026-09-13). The broker and carrier dispatch trip views share
 * the workspace payload; the carrier driver trip view loads its own smaller
 * one. Extracted unchanged from the former single /trips/[id] route.
 */

const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i

/**
 * Resolve a trip by its number (`ref_code`, the key the role routes use) or
 * by id (legacy `/trips/<id>` links). Returns null when the trip does not
 * exist or this user may not open it.
 */
export async function loadAccessibleTrip(user: SessionUser, key: string): Promise<Trip | null> {
  const admin = createAdminClient()
  const query = admin.from('trips').select('*')
  const { data } = UUID.test(key) ? await query.eq('id', key).maybeSingle() : await query.eq('ref_code', key).maybeSingle()
  if (!data) return null
  const trip = data as Trip
  if (!(await canAccessTrip(user, trip.id))) return null
  return trip
}

/** Everything the broker / carrier dispatch trip views render. */
export async function loadWorkspacePayload(user: SessionUser, trip: Trip) {
  const admin = createAdminClient()
  const id = trip.id
  // An admin not on the trip still gets every tool (Nash, 2026-09-17) — the
  // same synthetic row the API guard admits, so what the page shows and what
  // the API allows always match.
  const myParticipant = (await getMyParticipant(id)) ?? (user.role === 'admin' ? adminParticipantFor(id, user) : null)
  const { trips: myTrips } = await getVisibleTrips(user)

  const [participants, documents, permits, warnings, events, requests] = await Promise.all([
    admin.from('trip_participants').select('*').eq('trip_id', id).neq('status', 'removed').order('created_at'),
    admin.from('documents').select('*').eq('trip_id', id).order('created_at', { ascending: false }),
    admin.from('permits').select('*').eq('trip_id', id).order('created_at'),
    admin.from('warnings').select('*').eq('trip_id', id).eq('resolved', false).order('created_at'),
    admin.from('trip_events').select('*').eq('trip_id', id).order('created_at', { ascending: false }).limit(100),
    admin.from('service_requests').select('*').eq('trip_id', id).order('created_at', { ascending: false }),
  ])
  // Chat is read through the privacy filter — another participant's private
  // questions must never reach this page's payload (2026-09-07).
  const { messages: chat } = await loadVisibleChat(id, user.id)
  const shareChat = myParticipant?.share_chat !== false

  const docs = (documents.data ?? []) as TripDocument[]
  // Bucket files are signed; provider-hosted files (historical imports) pass
  // through — one resolver for both (2026-09-14).
  const signedUrls = await resolveDocumentUrls(docs)

  const completions = await getTripCompletions(user, [...new Set([...myTrips.map((t) => t.id), id])])
  const routeCredits = await getRouteCreditBalance(user)
  const [contacts, invite] = await Promise.all([loadContactPicker(user.id), loadInviteContacts(user)])
  // The carrier performing this trip — name, MC, DOT (Nash, 2026-09-19).
  // Attaches a dispatcher's single carrier relation by default.
  const carrier = await loadTripCarrier(trip, (participants.data ?? []) as TripParticipant[], { id: user.id, role: user.role, label: user.name || user.email })
  // Auto-added rows name who / which company brought them (§40, 2026-09-30).
  const people = await enrichParticipantProvenance((participants.data ?? []) as TripParticipant[], admin)
  const signedRequests = await signRouteFormats(
    ((requests.data ?? []) as ServiceRequest[]).map((r) => ({
      ...r,
      route_gpx_url: r.route_gpx_url ?? null,
      route_hummer_url: r.route_hummer_url ?? null,
    })),
  )

  return {
    myParticipant,
    myTrips,
    participants: people,
    documents: docs,
    signedUrls,
    // §14: cost fields never leave the server for a pilot, whatever access they hold.
    permits: stripPermitCostFor(myParticipant?.role ?? user.role, (permits.data ?? []) as Permit[]),
    warnings: activeDimensionWarnings((warnings.data ?? []) as TripWarning[]),
    events: (events.data ?? []) as TripEvent[],
    requests: signedRequests,
    chat,
    shareChat,
    completions,
    routeCredits,
    contacts: { drivers: contacts.drivers, brokers: contacts.brokers, invite: invite.contacts },
    carrier,
  }
}

/** Everything the carrier driver trip view renders. */
export async function loadDriverTripPayload(user: SessionUser, trip: Trip) {
  const admin = createAdminClient()
  const id = trip.id
  // An admin not on the trip still gets every tool (Nash, 2026-09-17) — the
  // same synthetic row the API guard admits, so what the page shows and what
  // the API allows always match.
  const myParticipant = (await getMyParticipant(id)) ?? (user.role === 'admin' ? adminParticipantFor(id, user) : null)
  const { trips: myTrips } = await getVisibleTrips(user)
  const [participants, documents, permits, warnings, events] = await Promise.all([
    admin.from('trip_participants').select('*').eq('trip_id', id).neq('status', 'removed').order('created_at'),
    admin.from('documents').select('*').eq('trip_id', id).order('created_at', { ascending: false }),
    admin.from('permits').select('*').eq('trip_id', id).order('created_at'),
    admin.from('warnings').select('*').eq('trip_id', id).eq('resolved', false).order('created_at'),
    admin.from('trip_events').select('*').eq('trip_id', id).order('created_at', { ascending: false }).limit(100),
  ])
  const { messages: chat } = await loadVisibleChat(id, user.id)
  const docs = (documents.data ?? []) as TripDocument[]
  // Permit files are keyed by document id (same map the workspace uses).
  const permitUrls = await resolveDocumentUrls(docs)
  const [completions, routeCredits, routeRequests, units, people] = await Promise.all([
    getTripCompletions(user, [...new Set([...myTrips.map((t) => t.id), id])]),
    getRouteCreditBalance(user),
    loadDriverRouteRequests([id]),
    loadDriverUnits([id]),
    // Auto-added rows name who / which company brought them (§40, 2026-09-30).
    enrichParticipantProvenance((participants.data ?? []) as TripParticipant[], admin),
  ])
  return {
    myParticipant,
    myTrips,
    participants: people,
    // §14: cost fields never leave the server for a pilot, whatever access they hold.
    permits: stripPermitCostFor(myParticipant?.role ?? user.role, (permits.data ?? []) as Permit[]),
    warnings: activeDimensionWarnings((warnings.data ?? []) as TripWarning[]),
    permitUrls,
    chat,
    shareChat: myParticipant?.share_chat !== false,
    completions,
    routeCredits,
    routeRequests,
    units,
    // Events are newest-first; the helper wants oldest-first so the latest invitation wins.
    pilotAccess: pilotAccessFromEvents([...((events.data ?? []) as TripEvent[])].reverse()),
  }
}
