/**
 * Pilot Company / Pilot Driver — domain rules (Phase 1 replica, 2026-09-12).
 *
 * Source: "HeavyHaul Agent Pilot Company and Pilot Driver Management Logic"
 * (implementation article). Nash, 2026-09-12: "right now, we're creating the
 * design… we don't use a real email, and we don't use the real credentials to
 * log in… everything that's test is for admin only to view."
 *
 * Pure data and pure functions only — no I/O. The dashboards and the signup
 * replica render from these so the rules live in exactly one place and the
 * later backend phase can reuse them unchanged.
 */

export type PilotAccountType = 'pilot_driver' | 'pilot_company'

/** Article §59 — every pilot document carries one of these. */
export type PilotDocumentStatus =
  | 'Not Uploaded'
  | 'Uploaded'
  | 'Pending Review'
  | 'Approved'
  | 'Rejected'
  | 'Expired'
  | 'Expiring Soon'

export interface PilotDocumentSlot {
  key: string
  label: string
  required: boolean
  /** Insurance, licenses and certifications expire; a W-9 only has an upload date (§59). */
  expires: boolean
}

/**
 * Article §10. The Utah / Washington slots are optional "unless the exact
 * legal requirement is verified and approved" — the UI must not claim any
 * state certification is mandatory, so the labels below are the article's.
 */
export const PILOT_DRIVER_DOCUMENTS: PilotDocumentSlot[] = [
  { key: 'w9', label: 'W-9', required: true, expires: false },
  { key: 'driver_license', label: 'Driver license', required: true, expires: true },
  { key: 'insurance', label: 'Certificate of insurance', required: true, expires: true },
  { key: 'pilot_certification', label: 'Pilot car / escort certification', required: true, expires: true },
  { key: 'state_certification', label: 'Optional State Certification', required: false, expires: true },
  { key: 'utah_certification', label: 'Utah Certification, if applicable', required: false, expires: true },
  { key: 'washington_certification', label: 'Washington Certification, if applicable', required: false, expires: true },
]

/** Article §11 — the business license is required, with a 30-day grace window (§11, Task C3). */
export const PILOT_COMPANY_DOCUMENTS: PilotDocumentSlot[] = [
  { key: 'insurance', label: 'Certificate of insurance', required: true, expires: true },
  { key: 'w9', label: 'W-9', required: true, expires: false },
  { key: 'business_license', label: 'Business license', required: true, expires: true },
  { key: 'good_standing', label: 'Certificate of good standing (optional)', required: false, expires: true },
  { key: 'company_documents', label: 'Company operating documents (optional)', required: false, expires: false },
  { key: 'state_company_certification', label: 'State-specific company certification, if applicable', required: false, expires: true },
]

export const BUSINESS_LICENSE_GRACE_DAYS = 30

export function documentSlotsFor(type: PilotAccountType): PilotDocumentSlot[] {
  return type === 'pilot_driver' ? PILOT_DRIVER_DOCUMENTS : PILOT_COMPANY_DOCUMENTS
}

/**
 * One line of paperwork as the carrier sees it on the "My Pilot" tab
 * (Nash, 2026-09-13): every slot of the pilot's account type, filled from
 * what the pilot uploaded, missing slots as "Not Uploaded".
 */
export interface PilotPaperworkDoc {
  key: string
  label: string
  required: boolean
  status: PilotDocumentStatus
  expirationDate?: string
  uploadedAt?: string
}

export function paperworkList(
  type: PilotAccountType,
  docs: Array<{ key: string; status: PilotDocumentStatus; expirationDate?: string; uploadedAt?: string }>,
): PilotPaperworkDoc[] {
  return documentSlotsFor(type).map((slot) => {
    const d = docs.find((x) => x.key === slot.key)
    return { key: slot.key, label: slot.label, required: slot.required, status: d?.status ?? 'Not Uploaded', expirationDate: d?.expirationDate, uploadedAt: d?.uploadedAt }
  })
}

/**
 * Nash: "we should have an alert in there that there is missing paperwork."
 * Required documents that are missing, rejected, expired or expiring soon —
 * one short phrase each, in slot order. Empty when the paperwork is in order.
 */
export function paperworkAlerts(docs: PilotPaperworkDoc[]): string[] {
  const out: string[] = []
  for (const d of docs) {
    if (!d.required) continue
    if (d.status === 'Not Uploaded') out.push(`${d.label} missing`)
    else if (d.status === 'Rejected') out.push(`${d.label} rejected`)
    else if (d.status === 'Expired') out.push(`${d.label} expired${d.expirationDate ? ` ${d.expirationDate}` : ''}`)
    else if (d.status === 'Expiring Soon') out.push(`${d.label} expires ${d.expirationDate ?? 'soon'}`)
  }
  return out
}

/** Article §35 — capability toggles, in the article's order. */
export const PILOT_CAPABILITIES = [
  'Lead pilot',
  'Chase / rear pilot',
  'High pole',
  'Steer / tillerman support',
  'Route survey',
  'Bucket / utility coordination, if applicable',
  'Police escort coordination, if applicable',
  'Oversize escort',
  'Superload escort',
  'Night movement support, if applicable',
  'Local city escort support',
] as const
export type PilotCapability = (typeof PILOT_CAPABILITIES)[number]

/** Article §35 — "Each capability can have a status". */
export type CapabilityStatus =
  | 'Self-declared'
  | 'Document uploaded'
  | 'Verified by admin'
  | 'Expired'
  | 'Rejected'

/** Article §33 — "Each pilot car/unit should require four photos". */
export const VEHICLE_PHOTO_SIDES = ['Front', 'Back', 'Left side', 'Right side'] as const
export type VehiclePhotoSide = (typeof VEHICLE_PHOTO_SIDES)[number]

/** Article §29. */
export type RelationshipStatus = 'Pending' | 'Approved' | 'Rejected' | 'Revoked' | 'Suspended'

/** Article §46 — the assignment status the broker sees. */
export type AssignmentStatus = 'invited' | 'accepted' | 'in progress' | 'completed'

/** Article §16 / §50 — the only three scope types in the MVP; no route segments. */
export type AccessScopeType = 'state' | 'permit' | 'full_trip'

/** Article §14 / §46 — "Pilot role, such as lead, chase, high pole". */
export type PilotPosition = 'Lead' | 'Chase' | 'High pole'

/** Article §60 — the readiness labels, verbatim. */
export type ReadinessStatus =
  | 'Incomplete Profile'
  | 'Email Not Verified'
  | 'Documents Missing'
  | 'Insurance Missing'
  | `Business License Due in ${number} Days`
  | 'Ready for Assignment'
  | 'Verified Pilot'
  | 'Expired Documents'
  | 'Admin Review Required'

export interface ReadinessInput {
  accountType: PilotAccountType
  name: string
  phone: string
  emailVerified: boolean
  /** Status per document slot key; a missing key counts as Not Uploaded. */
  documents: Record<string, PilotDocumentStatus | undefined>
  /** ISO date the account was created — drives the 30-day business license window. */
  createdAt: string
  /** ISO date "today"; injected so the rule is testable. */
  today: string
  /** Set by internal admin when the pilot has been verified (§60 "Verified Pilot"). */
  adminVerified?: boolean
}

function daysBetween(fromIso: string, toIso: string): number {
  const ms = new Date(toIso).getTime() - new Date(fromIso).getTime()
  return Math.floor(ms / 86_400_000)
}

/** Days left in the business license grace window; negative once it has passed. */
export function businessLicenseDaysLeft(createdAt: string, today: string): number {
  return BUSINESS_LICENSE_GRACE_DAYS - daysBetween(createdAt, today)
}

/**
 * Article §12 minimum before viewing shared permits + §60 labels. The first
 * failing rule, in the order a carrier would care about it, is the label.
 */
export function computeReadiness(input: ReadinessInput): ReadinessStatus {
  if (!input.name.trim() || !input.phone.trim()) return 'Incomplete Profile'
  if (!input.emailVerified) return 'Email Not Verified'

  const slots = documentSlotsFor(input.accountType)
  const statusOf = (key: string): PilotDocumentStatus => input.documents[key] ?? 'Not Uploaded'
  const uploaded = (key: string) => statusOf(key) !== 'Not Uploaded'

  if (slots.some((s) => statusOf(s.key) === 'Expired')) return 'Expired Documents'
  if (slots.some((s) => statusOf(s.key) === 'Rejected')) return 'Admin Review Required'
  if (!uploaded('insurance')) return 'Insurance Missing'

  if (input.accountType === 'pilot_company' && !uploaded('business_license')) {
    const left = businessLicenseDaysLeft(input.createdAt, input.today)
    // §11: past the window the account is flagged for admin review.
    if (left < 0) return 'Admin Review Required'
    // Still inside the grace window — the other required documents decide.
    const othersMissing = slots.some(
      (s) => s.required && s.key !== 'business_license' && !uploaded(s.key),
    )
    if (othersMissing) return 'Documents Missing'
    return `Business License Due in ${left} Days`
  }

  if (slots.some((s) => s.required && !uploaded(s.key))) return 'Documents Missing'
  return input.adminVerified ? 'Verified Pilot' : 'Ready for Assignment'
}

/** Article §12 — may this pilot open shared permits at all? */
export function canViewSharedPermits(status: ReadinessStatus): boolean {
  return (
    status === 'Ready for Assignment' ||
    status === 'Verified Pilot' ||
    status.startsWith('Business License Due in')
  )
}

/**
 * Article §15 / §51: "can_view_rate_confirmation = false always for pilot
 * users" — "Even if scope_type = full_trip". One function so every surface
 * (documents, AI, email links) asks the same question.
 */
export function pilotCanViewRateConfirmation(): false {
  return false
}

/** Article §22 — the access options offered on Manage Pilot Access. */
export const ACCESS_SCOPE_LABELS: Record<AccessScopeType, string> = {
  state: 'State access — every permit for that state, including permits uploaded later',
  permit: 'Permit access — only the selected permit; later permits are not shared',
  full_trip: 'Full trip access — the operational trip; the rate confirmation stays hidden',
}

/* ------------------------------------------------------------------ */
/* Onboarding after sign-in (Nash, 2026-09-12 feedback)                */
/* ------------------------------------------------------------------ */

/**
 * Nash: "I would like to have these three steps to let him in to join
 * [account type, details, validate email]. And then the other steps are done
 * on the page itself, on the my assignments page." So documents, vehicle and
 * capabilities are post-login tasks, listed as unfinished until complete.
 *
 * Who may upload the driver's documents: "if I'm independent, I have to
 * upload those. But if I work for a pilot company… they handle the step
 * four for me. I handle my step five and six… I could handle it too from my
 * terminal." A carrier dispatcher never uploads to a driver's profile —
 * "No. This is my personal."
 */
export interface OnboardingTask {
  step: 4 | 5 | 6
  title: string
  /** What is still missing, in the words the pilot sees. */
  missing: string[]
  /** Who is allowed to complete it. */
  owner: string
  done: boolean
}

export interface OnboardingInput {
  accountType: PilotAccountType
  /** Approved pilot company that handles documents on the driver's behalf, if any. */
  companyName?: string | null
  documents: Record<string, PilotDocumentStatus | undefined>
  /** Number of vehicle photos on the primary vehicle; null when no vehicle yet. */
  vehiclePhotos: number | null
  capabilityCount: number
}

export function onboardingTasks(input: OnboardingInput): OnboardingTask[] {
  const slots = documentSlotsFor(input.accountType)
  const missingDocs = slots
    .filter((s) => s.required && (input.documents[s.key] ?? 'Not Uploaded') === 'Not Uploaded')
    .map((s) => s.label)
  const docOwner =
    input.accountType === 'pilot_company'
      ? 'Your company'
      : input.companyName
        ? `You or ${input.companyName}`
        : 'Only you'
  const vehicleMissing =
    input.vehiclePhotos === null
      ? ['Add your vehicle']
      : input.vehiclePhotos < VEHICLE_PHOTO_SIDES.length
        ? [`${VEHICLE_PHOTO_SIDES.length - input.vehiclePhotos} of ${VEHICLE_PHOTO_SIDES.length} photos`]
        : []
  return [
    { step: 4, title: 'Required documents', missing: missingDocs, owner: docOwner, done: missingDocs.length === 0 },
    {
      step: 5,
      title: input.accountType === 'pilot_company' ? 'Pilot units' : 'Your vehicle',
      missing: vehicleMissing,
      owner: input.accountType === 'pilot_company' ? 'Your company' : 'Only you',
      done: vehicleMissing.length === 0,
    },
    {
      step: 6,
      title: 'Capabilities',
      missing: input.capabilityCount === 0 ? ['Tick what you can do'] : [],
      owner: input.accountType === 'pilot_company' ? 'Your company' : 'Only you',
      done: input.capabilityCount > 0,
    },
  ]
}

/* ------------------------------------------------------------------ */
/* Pilot access chosen at invite time (Nash, 2026-09-12 feedback)      */
/* ------------------------------------------------------------------ */

/**
 * Nash: "when he goes to invite the pilot… we need to ask what type of
 * access that pilot will have… all states or select certain states or
 * certain permits… if he doesn't choose anything, he can choose all decide
 * later and send that invitation."
 */
export type PilotInviteAccess =
  | { type: 'full_trip' }
  | { type: 'states'; states: string[] }
  | { type: 'permits'; permit_ids: string[]; labels?: string[] }
  | { type: 'decide_later' }

/** One line for the trip history and the pilot's "Shared with you" card. */
export function describePilotAccess(access: PilotInviteAccess | null | undefined): string {
  if (!access) return 'Decide later'
  switch (access.type) {
    case 'full_trip':
      return 'Full trip (all states)'
    case 'states':
      return access.states.length ? `States: ${access.states.join(', ')}` : 'Decide later'
    case 'permits':
      return access.permit_ids.length
        ? `Permits: ${(access.labels?.length ? access.labels : access.permit_ids).join(', ')}`
        : 'Decide later'
    default:
      return 'Decide later'
  }
}

/**
 * Nash: "The trip has access for a pilot dispatch and the access will be
 * replicated to any pilots that he invites to this trip… If the trip was
 * shared with a pilot driver for only one state and that pilot driver will
 * add a dispatch into this… that pilot dispatch also will see only that one
 * [state]." A pilot can never hand out more than they hold.
 */
export function inheritedPilotAccess(inviter: PilotInviteAccess | null | undefined): PilotInviteAccess {
  return inviter ?? { type: 'decide_later' }
}

/**
 * Who a pilot may add to a trip. Nash: a pilot dispatch adds pilots
 * (drivers); a pilot driver "can add only a dispatch into the trip".
 */
export const PILOT_INVITE_TARGETS: Record<PilotAccountType, string[]> = {
  pilot_company: ['Pilot driver'],
  pilot_driver: ['Pilot dispatch'],
}

/**
 * Nash: "if the pilot dispatch did not upload the documents… he should not be
 * able to open the trip info, which is the trip workspace. He should get an
 * alert that uploading the documents is required before moving forward…
 * COI, W-9, business license, they required." Pilot dispatch only — the
 * driver keeps viewing with a reminder on top (earlier feedback the same day).
 */
export const PILOT_COMPANY_WORKSPACE_DOCS = ['insurance', 'w9', 'business_license'] as const

export function missingWorkspaceDocs(
  documents: Record<string, PilotDocumentStatus | undefined>,
): PilotDocumentSlot[] {
  return PILOT_COMPANY_DOCUMENTS.filter(
    (s) =>
      (PILOT_COMPANY_WORKSPACE_DOCS as readonly string[]).includes(s.key) &&
      (documents[s.key] ?? 'Not Uploaded') === 'Not Uploaded',
  )
}

/* ------------------------------------------------------------------ */
/* Pilot cars on the carrier driver's state cards (Nash, 2026-09-12)   */
/* ------------------------------------------------------------------ */

/**
 * The access each pilot was given, read back from the trip history: the
 * invite route writes `participant_invited` with `pilot_access` (Task F1
 * replica). Latest event per email wins. Keyed by lower-cased email.
 */
export function pilotAccessFromEvents(
  events: { action: string; detail: Record<string, unknown> | null }[],
): Record<string, string> {
  const out: Record<string, string> = {}
  for (const e of events) {
    if (e.action !== 'participant_invited') continue
    const email = typeof e.detail?.email === 'string' ? e.detail.email.toLowerCase() : null
    const access = typeof e.detail?.pilot_access === 'string' ? e.detail.pilot_access : null
    if (email && access) out[email] = access
  }
  return out
}

/**
 * Does an access line (as written by describePilotAccess) cover a state?
 * "Full trip (all states)" → every state; "States: OH, PA" → listed states;
 * "Permits: OH · 123, PA · 456" → the states in front of each permit label;
 * "Decide later" / unknown → none.
 */
export function pilotAccessCoversState(access: string | undefined, state: string): boolean {
  if (!access || !state) return false
  if (access.startsWith('Full trip')) return true
  if (access.startsWith('States: ')) return access.slice(8).split(',').map((x) => x.trim()).includes(state)
  if (access.startsWith('Permits: ')) {
    return access
      .slice(9)
      .split(',')
      .map((x) => x.trim().split(' · ')[0])
      .includes(state)
  }
  return false
}
