import 'server-only'
import { createAdminClient } from '@/lib/supabase/admin'
import { getMyParticipant, type SessionUser } from '@/lib/auth'
import { canAccessTrip, getVisibleTrips, getTripCompletions } from '@/lib/data/trips'
import { loadDriverRouteRequests, loadDriverUnits, signRouteFormats } from '@/lib/data/driver'
import { loadVisibleChat } from '@/lib/data/chat'
import { getRouteCreditBalance } from '@/lib/data/credits'
import { loadContactPicker } from '@/lib/data/contacts'
import { pilotAccessFromEvents } from '@/lib/domain/pilot'
import type {
  Permit,
  ServiceRequest,
  Trip,
  TripDocument,
  TripEvent,
  TripParticipant,
  TripWarning,
} from '@/types/db'

/**
 * 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
  const myParticipant = await getMyParticipant(id)
  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[]
  const signedUrls: Record<string, string> = {}
  await Promise.all(
    docs.map(async (d) => {
      const { data } = await admin.storage.from('trip-documents').createSignedUrl(d.storage_path, 60 * 30)
      if (data?.signedUrl) signedUrls[d.id] = data.signedUrl
    }),
  )

  const completions = await getTripCompletions(user, [...new Set([...myTrips.map((t) => t.id), id])])
  const routeCredits = await getRouteCreditBalance(user)
  const contacts = await loadContactPicker(user.id)
  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: (participants.data ?? []) as TripParticipant[],
    documents: docs,
    signedUrls,
    permits: (permits.data ?? []) as Permit[],
    warnings: (warnings.data ?? []) as TripWarning[],
    events: (events.data ?? []) as TripEvent[],
    requests: signedRequests,
    chat,
    shareChat,
    completions,
    routeCredits,
    contacts: { drivers: contacts.drivers, brokers: contacts.brokers },
  }
}

/** Everything the carrier driver trip view renders. */
export async function loadDriverTripPayload(user: SessionUser, trip: Trip) {
  const admin = createAdminClient()
  const id = trip.id
  const myParticipant = await getMyParticipant(id)
  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: Record<string, string> = {}
  await Promise.all(
    docs.map(async (d) => {
      const { data } = await admin.storage.from('trip-documents').createSignedUrl(d.storage_path, 60 * 30)
      if (data?.signedUrl) permitUrls[d.id] = data.signedUrl
    }),
  )
  const [completions, routeCredits, routeRequests, units] = await Promise.all([
    getTripCompletions(user, [...new Set([...myTrips.map((t) => t.id), id])]),
    getRouteCreditBalance(user),
    loadDriverRouteRequests([id]),
    loadDriverUnits([id]),
  ])
  return {
    myParticipant,
    myTrips,
    participants: (participants.data ?? []) as TripParticipant[],
    permits: (permits.data ?? []) as Permit[],
    warnings: (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()),
  }
}
