import type { PaginatedResponse } from "@edr/types";

export type FreightType = "CONTAINER" | "BULK" | "MIXED";

export type SchedulingStatus =
  | "NOT_SCHEDULED"
  | "HOLDING"
  | "ELIGIBLE"
  | "SCHEDULED"
  | "DISPATCHED"
  | "WAITING_FOR_WAGON";

export type TrainScheduleStatus =
  | "DRAFT"
  | "SCHEDULED"
  | "DISPATCHED"
  | "ARRIVED"
  | "CANCELLED";

export interface YardOption {
  id: string;
  name: string;
  code: string;
  country?: string;
}

export interface EligibleContainerBooking {
  id: string;
  reference: string;
  freightType?: FreightType | string;
  customer: string;
  containerType: string;
  quantity: number;
  weightTons: number;
  origin: string;
  destination: string;
  preferredDepartureDate: string;
  status: string;
  schedulingStatus?: SchedulingStatus;
  priorityScore?: number;
}

export interface EligibleContainerBookingsResponse {
  count: number;
  items: EligibleContainerBooking[];
}

export interface WagonPlanAllocation {
  bookingId: string;
  bookingReference: string;
  allocatedWeightTons: number;
  loadType?: string;
}

export interface WagonPlanRow {
  sequenceNo: number;
  capacityTons: number;
  lengthMeters: number;
  assignedWeightTons: number;
  slotLoadType?: "CONTAINER" | "BULK";
  wagonTypeCode?: string;
  allocations: WagonPlanAllocation[];
}

export interface ContainerUnitRow {
  bookingId: string;
  bookingReference: string;
  bookingContainerId: string;
  unitIndex: number;
  containerTypeId: string;
  containerTypeCode: string;
  label: string;
  grossWeightTons: number;
  sizeFt?: number;
  containersPerWagon?: number;
  teuSlots?: number;
  containerNumber?: string | null;
}

export interface ContainerPlacement {
  bookingContainerId: string;
  unitIndex: number;
  sequenceNo: number;
  containerId?: string;
  containerNumber?: string;
  sealNumber?: string;
}

export interface FleetAvailabilityRow {
  wagonTypeId: string;
  wagonTypeCode: string;
  needed: number;
  available: number;
  shortfall: number;
}

/** Per-booking wagon shortage: how many wagons of which type the booking still lacks. */
export interface BookingWagonShortage {
  wagonTypeCodes: string;
  wagonsNeeded: number;
  wagonsAvailable: number;
  wagonsShort: number;
}

export interface DeferredBookingRow {
  id: string;
  reference: string;
  reason: string;
  /** Set when the deferral is a fleet-stock shortage (absent for config issues). */
  shortage?: BookingWagonShortage | null;
}

export interface TrainSchedulingGlobalRules {
  id: string;
  maxWagonsPerTrain: number;
  importWindowLeadDays: number;
  exportBookingLeadHours: number;
  windowOpenHour: number;
  windowCloseHour: number;
  windowDurationHours: number;
  docReviewMinutes: number;
  paymentWindowMinutes: number;
  /** Minutes before departure the import window closes; null = close at departure. */
  importCloseOffsetMinutes: number | null;
  /** Minutes before departure the export window closes; null = close at departure. */
  exportCloseOffsetMinutes: number | null;
}

export interface TrainSchedulePreviewResponse {
  valid: boolean;
  violations: string[];
  warnings: string[];
  fleetAvailability?: FleetAvailabilityRow[];
  deferredBookings?: DeferredBookingRow[];
  summary: {
    totalBookings: number;
    /** Cargo VGM only — display gross instead. */
    totalWeightTons: number;
    /** GROSS: cargo + the tare of every wagon in the plan. */
    grossWeightTons: number;
    totalTareTons: number;
    wagonType: string;
    wagonsNeeded: number;
    totalLengthMeters: number;
    freightMode?: FreightType;
  };
  bookingIds: string[];
  wagonPlan: WagonPlanRow[];
  containerUnits?: ContainerUnitRow[];
  containerSlotSequenceNos?: number[];
}

export interface LocomotiveRecord {
  id: string;
  code: string;
  name?: string | null;
  maxPullWeightTons: number;
  maxTrainLengthMeters: number;
  status: "AVAILABLE" | "ASSIGNED" | "MAINTENANCE" | "OUT_OF_SERVICE";
  currentYardId?: string | null;
  locomotiveType?: "DIESEL" | "ELECTRIC";
  /** Whether the locomotive is currently at the route's origin yard. */
  atOriginYard?: boolean;
  /** Number of upcoming schedules this locomotive is already assigned to. */
  futureScheduleCount?: number;
}

export interface TrainScheduleListItem {
  id: string;
  reference?: string | null;
  createdAt?: string | null;
  scheduleDate: string;
  trainNumber?: string | null;
  /** Trade direction of this departure (IMPORT / EXPORT), when known. */
  direction?: string | null;
  routeName?: string | null;
  origin: string | null;
  destination: string | null;
  freightType?: FreightType | null;
  /** Built train (Train Builder) behind this departure, when scheduled by train. */
  train?: {
    id: string;
    code: string;
    trainName?: string | null;
  } | null;
  locomotive:
    | {
        id: string;
        code: string;
        name?: string | null;
        currentYardId?: string | null;
      }
    | null;
  /** All locomotives pulling this train (≥2). Falls back to `locomotive` for legacy rows. */
  locomotives?: Array<{
    id: string;
    code: string;
    name?: string | null;
    currentYardId?: string | null;
  }>;
  wagonCount: number;
  totalWeightTons: number;
  totalLengthMeters: number;
  bookingsCount: number;
  status: TrainScheduleStatus | string;
}

export type TrainScheduleSortField =
  | "createdAt"
  | "scheduledDepartureDate"
  | "reference"
  | "trainNumber"
  | "status";

/** Server-side query for the paginated train-schedule list. */
export interface TrainScheduleListFilters {
  page?: number;
  pageSize?: number;
  /** Matches schedule reference, train number, route yards, stations, locomotive code. */
  search?: string;
  /** Lifecycle status (exact match). */
  status?: TrainScheduleStatus;
  /** Derived from the bookings aboard: CONTAINER/BULK = only that kind; MIXED = both. */
  freightType?: FreightType;
  /** Origin station/yard id (exact match). */
  originStationId?: string;
  /** Destination station/yard id (exact match). */
  destinationStationId?: string;
  sortBy?: TrainScheduleSortField;
  sortOrder?: "ASC" | "DESC";
}

export type TrainScheduleListResponse = PaginatedResponse<TrainScheduleListItem>;

export interface BookableSchedule {
  id: string;
  scheduleDate: string;
  trainNumber?: string | null;
  routeName?: string | null;
  origin: string | null;
  destination: string | null;
  freightType?: FreightType | null;
  status: TrainScheduleStatus | string;
  bookingWindowStatus: "OPEN" | "FULL" | "CLOSED" | string;
  maxWagons: number;
  remainingWagons: number;
  locomotive: { id: string; code: string; name?: string | null } | null;
}

/**
 * Import booking-cycle phase for a schedule's booking window (null for
 * legacy/DOMESTIC schedules that don't run the one-day cycle).
 */
export type BookingWindowPhase =
  | "PRE_WINDOW"
  | "OPEN"
  | "DOC_REVIEW"
  | "PAYMENT"
  | "CLOSED_FOR_DAY"
  | "DONE";

export type BatchBoardBookingState =
  | "ALLOCATED"
  | "SELECTED_FOR_BATCH"
  | "READY"
  | "WAITING"
  | "PENDING_CONTRACT"
  | "EXPIRED";

export interface BatchBoardBooking {
  id: string;
  reference: string;
  company: string;
  isGovernment: boolean;
  wagons: number;
  weightTons: number;
  lengthMeters: number;
  paymentDeadline: string | null;
  state: BatchBoardBookingState;
  /** Rule-engine priority score used to rank the batch (higher = boards first). */
  priorityScore: number;
  /** CONTAINER | BULK — for the priority-tracking visuals. */
  freightType: string | null;
}

/**
 * An announced booking window on any lane (import cycle or export FCFS), for
 * staff dashboards. Mirrors the customer portal's MyBookingWindow.
 */
/**
 * The nearest document-review deadline that still has booking requests nobody
 * accepted or rejected. Everything still pending when it lapses is expired by
 * the window engine, so the header counts down to it.
 */
export interface DocReviewAlert {
  scheduleId: string;
  originYardId: string;
  destinationYardId: string;
  /** EAT booking day, YYYY-MM-DD. */
  day: string;
  /** IMPORT (the usual) or DOMESTIC — the direction the at-risk requests belong to. */
  tradeDirection: string;
  docReviewEndsAt: string;
  /** Full length of the review phase — warn past its halfway mark. */
  docReviewMinutes: number;
  pendingCount: number;
}

export interface StaffBookingWindow {
  scheduleId: string;
  reference: string | null;
  trainNumber: string | null;
  direction: "IMPORT" | "EXPORT" | null;
  windowPhase: BookingWindowPhase | string | null;
  isOpenNow: boolean;
  windowOpensAt: string | null;
  windowClosesAt: string | null;
  docReviewEndsAt: string | null;
  paymentPhaseEndsAt: string | null;
  bookingWindowStatus: string;
  bookingCycleNo: number;
  departureDate: string;
  origin: string | null;
  destination: string | null;
}

export interface BatchBoardSchedule {
  scheduleId: string;
  /** Human-facing schedule reference (S-YYYY-NNNNN). */
  scheduleReference: string | null;
  trainNumber: string | null;
  routeName: string | null;
  origin: string | null;
  destination: string | null;
  scheduleDate: string | null;
  createdAt: string | null;
  status: string;
  bookingWindowStatus: string;
  direction: string | null;
  windowPhase: BookingWindowPhase | null;
  windowOpensAt: string | null;
  windowClosesAt: string | null;
  docReviewEndsAt: string | null;
  paymentPhaseEndsAt: string | null;
  bookingCycleNo: number;
  /** Built train (Train Builder) behind this departure, when scheduled by train. */
  train: {
    id: string;
    code: string;
    trainName: string | null;
  } | null;
  locomotive: {
    code: string;
    name: string | null;
    maxPullWeightTons: number;
    maxTrainLengthMeters: number;
  } | null;
  capacity: {
    allocatedWagons: number;
    allocatedLengthMeters: number;
    /** Train-length cap: locomotive floored by global rules, plus overage tolerance. */
    maxLengthMeters: number | null;
    /** GROSS tons on the train — wagon tare + cargo, since the pull limit hauls both. */
    usedWeightTons: number;
    /** Pull-weight cap: locomotive floored by global rules, plus overage tolerance. */
    maxWeightTons: number | null;
    /** Wagon-slot cap for the train, derived from train length and the shortest wagon type. */
    maxWagons: number | null;
  };
  counts: {
    allocated: number;
    selectedForBatch: number;
    ready: number;
    waiting: number;
    pendingContract: number;
    expired: number;
  };
  bookings: BatchBoardBooking[];
}

export type BatchBoardSortField =
  | "createdAt"
  | "scheduledDepartureDate"
  | "trainNumber"
  | "status";

/** Server-side filters for the paginated batch board list. */
export interface BatchBoardFilters {
  page?: number;
  pageSize?: number;
  /** Subset of schedule statuses; omit for all (incl. arrived/cancelled). */
  statuses?: TrainScheduleStatus[];
  bookingWindowStatus?: "OPEN" | "FULL" | "CLOSED";
  /** Matches train number, route yards, stations, locomotive code. */
  search?: string;
  departureFrom?: string;
  departureTo?: string;
  createdFrom?: string;
  createdTo?: string;
  sortBy?: BatchBoardSortField;
  sortOrder?: "ASC" | "DESC";
}

/** Paginated batch-board list in the shared `{items, meta}` envelope. */
export type BatchBoardListResponse = PaginatedResponse<BatchBoardSchedule>;

export type BookingAllocationStatus =
  | "NOT_ATTEMPTED"
  | "ASSIGNED"
  | "DEFERRED"
  | "FAILED";

export interface BatchBoardBookingDetail extends BatchBoardBooking {
  fullyExecutedAt: string | null;
  selectedForBatchAt: string | null;
  allocationStatus: BookingAllocationStatus;
  allocationIssue: string | null;
  consolidationPartnerId: string | null;
  consolidationPartnerRef: string | null;
}

export interface BatchBoardCounts {
  allocated: number;
  selectedForBatch: number;
  ready: number;
  waiting: number;
  expired: number;
  pendingContract: number;
}

/** A booking bucket on the detail board (in-window vs pending-contract). */
export interface BatchBoardBucket {
  counts: BatchBoardCounts;
  bookings: BatchBoardBookingDetail[];
}

export interface BatchBoardScheduleDetail {
  scheduleId: string;
  /** Human-facing schedule reference (S-YYYY-NNNNN). */
  scheduleReference: string | null;
  trainNumber: string | null;
  routeName: string | null;
  origin: string | null;
  destination: string | null;
  scheduleDate: string | null;
  status: string;
  bookingWindowStatus: string;
  direction: string | null;
  windowPhase: BookingWindowPhase | null;
  windowOpensAt: string | null;
  windowClosesAt: string | null;
  docReviewEndsAt: string | null;
  paymentPhaseEndsAt: string | null;
  bookingCycleNo: number;
  /** Built train (Train Builder) behind this departure, when scheduled by train. */
  train: BatchBoardSchedule["train"];
  locomotive: BatchBoardSchedule["locomotive"];
  capacity: BatchBoardSchedule["capacity"];
  counts: BatchBoardSchedule["counts"];
  /** Bookings inside the schedule's booking window (fully-executed contracts). */
  bookings: BatchBoardBookingDetail[];
  pendingContract: BatchBoardBucket;
  allocationViolations: string[];
}

/**
 * A booking window for one of a contract's routes/schedules. `isOpenNow === true`
 * means a booking may be created right now for that route. Times are ISO strings;
 * render them in EAT (Africa/Addis_Ababa).
 */
export interface BookingWindow {
  scheduleId: string;
  direction: string | null;
  windowPhase: BookingWindowPhase | null;
  isOpenNow: boolean;
  windowOpensAt: string | null;
  windowClosesAt: string | null;
  docReviewEndsAt: string | null;
  paymentPhaseEndsAt: string | null;
  bookingWindowStatus: string;
  bookingCycleNo: number;
  departureDate: string;
  origin: string | null;
  destination: string | null;
}

export interface WagonAllocationAttemptResult {
  assignedBookingIds: string[];
  deferred: Array<{ id: string; reference: string; reason: string }>;
  issues: Array<{
    bookingId: string;
    status: BookingAllocationStatus;
    issue: string | null;
  }>;
  violations: string[];
}

export interface TrainScheduleWagonAllocation {
  id: string;
  bookingId: string;
  bookingReference: string | null;
  allocatedWeightTons: number;
  loadType?: string | null;
  status?: string;
  containerItems?: Array<{
    id: string;
    containerNumber: string | null;
    containerTypeId: string;
    grossWeightTons: number | null;
    containerId?: string | null;
    positionOnWagon?: number | null;
    bookingContainerId?: string | null;
  }>;
  bulkLoad?: {
    id: string;
    weightTons: number;
    cargoDescription: string | null;
  } | null;
}

/** Per-schedule booking-window rule snapshot (null fields fall back to global config). */
export interface ScheduleWindowRule {
  windowOpenHour: number | null;
  windowCloseHour: number | null;
  windowDurationHours: number | null;
  importWindowLeadDays: number | null;
  exportBookingLeadHours: number | null;
  /** Live global values (not snapshotted per schedule) — editor prefill baseline. */
  docReviewMinutes: number;
  paymentWindowMinutes: number;
}

/** Editable window-rule override for one schedule; every field optional. */
export interface UpdateScheduleWindowRulePayload {
  windowOpenHour?: number;
  windowCloseHour?: number;
  windowDurationHours?: number;
  docReviewMinutes?: number;
  paymentWindowMinutes?: number;
  importWindowLeadDays?: number;
  exportBookingLeadHours?: number;
}

export interface TrainScheduleDetail {
  id: string;
  reference?: string | null;
  status: TrainScheduleStatus | string;
  deferredBookings?: DeferredBookingRow[];
  freightType?: FreightType | null;
  trainNumber?: string | null;
  /** Wagon cap for this departure (built-train consist size or configured limit). */
  maxWagons?: number | null;
  /** Built train (Train Builder) behind this departure, when scheduled by train. */
  train?: {
    id: string;
    code: string;
    trainName?: string | null;
  } | null;
  direction?: string | null;
  /** Wagon order reversed on this train (physically-last wagon = position 1). */
  reverseWagonOrder?: boolean;
  /** True when this schedule needs loading confirmed before dispatch (import-Djibouti). */
  requiresLoadingConfirmation?: boolean;
  /** True when loading is already confirmed (or not required for this direction). */
  loadingConfirmed?: boolean;
  windowPhase?: BookingWindowPhase | string | null;
  windowOpensAt?: string | null;
  windowClosesAt?: string | null;
  docReviewEndsAt?: string | null;
  paymentPhaseEndsAt?: string | null;
  /** Booking-window rule snapshot — prefills the per-schedule settings editor. */
  windowRule?: ScheduleWindowRule | null;
  route?: {
    id: string;
    name: string;
  } | null;
  scheduledDepartureDate: string;
  scheduledArrivalDate?: string | null;
  actualDepartureAt?: string | null;
  actualArrivalAt?: string | null;
  originStation?: {
    id: string;
    label?: string;
    code?: string;
  } | null;
  destinationStation?: {
    id: string;
    label?: string;
    code?: string;
  } | null;
  trainSet?: {
    id: string;
    status: string;
    wagonCount: number;
    totalWeightTons: number;
    totalLengthMeters: number;
    locomotive?: {
      id: string;
      code: string;
      name?: string | null;
      status: string;
      currentYardId?: string | null;
      maxPullWeightTons: number;
      maxTrainLengthMeters?: number;
    } | null;
    /** All locomotives pulling this train (≥2). Falls back to `locomotive` for legacy rows. */
    locomotives?: Array<{
      id: string;
      code: string;
      name?: string | null;
      status: string;
      currentYardId?: string | null;
      maxPullWeightTons: number;
      maxTrainLengthMeters?: number;
    }>;
    wagons: Array<{
      id: string;
      sequenceNo: number;
      /**
       * Place in the drawn consist, 1..n — the built train's real coupling
       * order (reversed for a reverseWagonOrder schedule). Label wagons with
       * this, not sequenceNo: a slot's stored sequenceNo is not its position.
       */
      position?: number;
      capacityTons: number;
      lengthMeters: number;
      assignedWeightTons: number;
      /** Empty-wagon weight from the wagon type — gross = tare + cargo. */
      tareWeightTons?: number | null;
      status?: string;
      physicalWagonId?: string | null;
      physicalWagonNumber?: string | null;
      wagonType?: {
        id: string;
        code: string;
        name: string;
      } | null;
      allocations: TrainScheduleWagonAllocation[];
      /**
       * Coupled-but-empty wagon of the built train — no TrainSetWagon slot
       * behind it, so remove/edit actions do not apply.
       */
      consistOnly?: boolean;
    }>;
  } | null;
  bookings: Array<{
    id: string;
    reference: string | null;
    customer: string | null;
    weightTons: number;
    status: string | null;
    schedulingStatus?: SchedulingStatus | null;
    freightType?: FreightType | string | null;
    /** DOMESTIC = intercity ride-along; rides only its own leg below. */
    tradeDirection?: string | null;
    originYardId?: string | null;
    destinationYardId?: string | null;
    origin?: string | null;
    destination?: string | null;
    wagonsRequired?: number | null;
    loadedAt?: string | null;
    arrivedAt?: string | null;
    loadingStatus?: "LOADED" | "UNLOADED";
    wagonAssigned?: boolean;
  }>;
  /** Ordered corridor stops (route milestones) — for per-segment occupancy. */
  stops?: Array<{ yardId: string; label: string }>;
  /** Loco pull ceiling incl. overage tolerance — per-leg gross is held to it. */
  maxGrossWeightTons?: number | null;
  warnings?: string[];
}

export type ImportDjiboutiDocumentType =
  | "GATE_PASS"
  | "DELIVERY_ORDER"
  | "PORT_INVOICE"
  | "DJIBOUTI_T1"
  | "ETHIOPIA_T1"
  | "RAILWAY_BILL";

export interface ImportDjiboutiDocumentRecord {
  fileId?: string | null;
  fileUrl?: string | null;
  reference?: string | null;
  uploadedAt: string;
  uploadedBy?: string | null;
  notes?: string | null;
}

export type LoadingStatus = "LOADED" | "UNLOADED";

export interface ImportLoadingBooking {
  id: string;
  reference: string | null;
  customer: string | null;
  weightTons: number;
  loadingStatus: LoadingStatus;
}

export interface ImportLoadingBookingsResponse {
  count: number;
  items: ImportLoadingBooking[];
}

export interface ImportDjiboutiOperation {
  trainScheduleId: string;
  trainNumber: string | null;
  direction: string | null;
  status: {
    documentsComplete: boolean;
    missingDocuments: ImportDjiboutiDocumentType[];
    gatepassStatus: "SECURED" | "NOT_SECURED";
    gatepassGranted: boolean;
    readyForLoading: boolean;
    loadedOnTrain: boolean;
    departedFromDjibouti: boolean;
    loadListGenerated: boolean;
  };
  documents: Partial<Record<ImportDjiboutiDocumentType, ImportDjiboutiDocumentRecord>>;
  gatepassGrantedAt: string | null;
  gatepassSecuredAt?: string | null;
  gatepassStatus: "SECURED" | "NOT_SECURED";
  readyForLoadingAt: string | null;
  loadedOnTrainAt: string | null;
  departedFromDjiboutiAt: string | null;
  loadListGeneratedAt: string | null;
  performedBy: string | null;
  notes: string | null;
}

export interface UploadImportDjiboutiDocumentPayload {
  documentType: ImportDjiboutiDocumentType;
  fileId?: string;
  fileUrl?: string;
  reference?: string;
  notes?: string;
  performedBy?: string;
}

export interface ImportDjiboutiActionPayload {
  securedAt?: string;
  fileId?: string;
  fileUrl?: string;
  reference?: string;
  notes?: string;
  performedBy?: string;
}

export interface ImportDjiboutiLoadList {
  generatedAt: string;
  trainScheduleId: string;
  trainNumber: string | null;
  route: string | null;
  origin: string | null;
  destination: string | null;
  totalBookings: number;
  wagons: Array<{
    sequenceNo: number;
    wagonNumber: string | null;
    allocations: Array<{
      bookingId: string;
      bookingReference: string | null;
      loadType: string | null;
      allocatedWeightTons: number;
      containerNumbers: string[];
    }>;
  }>;
  operation: ImportDjiboutiOperation;
}

export type TrainCheckpointKind = "DEPARTED" | "PASSED" | "ARRIVED";

export interface TrackStation {
  sequenceNo: number;
  yardId: string;
  label: string;
  code: string;
}

export interface TrainCheckpoint {
  id: string;
  sequenceNo: number;
  yardId: string;
  label: string | null;
  kind: TrainCheckpointKind;
  occurredAt: string;
  note: string | null;
}

export interface TrainTrackResponse {
  scheduleId: string;
  status: TrainScheduleStatus | string;
  direction?: string | null;
  trainNumber?: string | null;
  actualDepartureAt?: string | null;
  actualArrivalAt?: string | null;
  origin: string | null;
  destination: string | null;
  stations: TrackStation[];
  currentSequenceNo: number;
  checkpoints: TrainCheckpoint[];
}

export interface RecordCheckpointPayload {
  sequenceNo: number;
  kind?: TrainCheckpointKind;
  occurredAt?: string;
  note?: string;
}

export interface TrainScheduleFilters {
  originStationId?: string;
  destinationStationId?: string;
  schedulingStatus?: SchedulingStatus;
  /** Scope eligible bookings to a single schedule (batch parity). */
  trainScheduleId?: string;
}

export interface TrainSchedulePreviewPayload {
  bookingIds: string[];
  scheduleDate: string;
  originStationId: string;
  destinationStationId: string;
  targetScheduleId?: string;
  maxTrainWeightTons?: number;
  maxTrainLengthMeters?: number;
  maxWagonsPerTrain?: number;
}

export interface RescheduleBookingSummary {
  id: string;
  reference: string;
  isGovernment: boolean;
  priorityScore: number;
  governmentInstitution?: string | null;
}

export interface ReschedulePlan {
  scheduleId: string;
  trigger: string;
  retained: RescheduleBookingSummary[];
  displaced: RescheduleBookingSummary[];
  readmitted: RescheduleBookingSummary[];
  finalBookingIds: string[];
  warnings: string[];
}

export interface CreateTrainSchedulePayload {
  routeId: string;
  scheduleDate: string;
  /** Built train (Train Builder) to run this departure — its locomotives are used. */
  trainId?: string;
  /** Hand-picked locomotives (minimum 2 — front and back). Ignored when trainId is set. */
  locomotiveIds?: string[];
  maxTrainWeightTons?: number;
  maxTrainLengthMeters?: number;
  maxWagonsPerTrain?: number;
  /** Reverse the wagon order on this train: physically-last wagon becomes position 1. */
  reverseWagonOrder?: boolean;
}

export interface AssignBookingsPayload {
  bookingIds: string[];
  forceAssign?: boolean;
  containerPlacements?: ContainerPlacement[];
  maxTrainWeightTons?: number;
  maxTrainLengthMeters?: number;
  maxWagonsPerTrain?: number;
}

export interface PinWagonAssignment {
  trainSetWagonId: string;
  physicalWagonId: string;
}

export interface PinWagonsPayload {
  assignments: PinWagonAssignment[];
}

export interface CompositionUnassignedBooking {
  id: string;
  reference: string | null;
  freightType: FreightType | null;
  priorityScore: number;
  cargoTotalWeightVgm: number;
  /** GROSS: cargo VGM + tare of every wagon the booking occupies. */
  grossWeightTons: number;
  status: string | null;
  schedulingStatus: SchedulingStatus | null;
  wagonsRequired: number;
  requiredWagonTypeCode: string;
  yardWagonsAvailable: number;
  canAssign: boolean;
  blockReason: string | null;
  shortage?: BookingWagonShortage | null;
}

export interface UnassignedBookingsResponse {
  fleetAtOrigin: FleetAvailabilityRow[];
  bookings: CompositionUnassignedBooking[];
}

export interface CompositionRemovalEntry {
  id: string;
  scheduleId: string;
  bookingId: string;
  bookingReference: string | null;
  removedByUserId: string | null;
  removedAt: string;
  notes: string | null;
}

// ── Intercity ride-along ─────────────────────────────────────────────────────
// Intercity (DOMESTIC) bookings have no train of their own — they ride a
// passing import/export schedule whose route milestones contain the booking's
// origin before its destination. Staff accept them at finalize time against
// the train's remaining wagon/weight/length capacity.

export interface IntercityCapacity {
  // Each axis can be null when the schedule's train/locomotive has no limit
  // configured for it (e.g. no max length on the loco) — the API passes the
  // gap through rather than inventing a number.
  wagons: number | null;
  weightTons: number | null;
  lengthMeters: number | null;
}

export interface IntercityBookingRow {
  id: string;
  reference: string | null;
  status: string;
  freightType: FreightType | null;
  isGovernment: boolean;
  customer: string;
  originYardId: string;
  destinationYardId: string;
  origin: string;
  destination: string;
  weightTons: number;
  paymentDeadline: string | null;
  need: IntercityCapacity | null;
}

export interface IntercityCandidateRow extends IntercityBookingRow {
  fits: boolean;
}

export interface IntercityCandidatesResult {
  scheduleId: string;
  routeId: string | null;
  remaining: IntercityCapacity | null;
  candidates: IntercityCandidateRow[];
  accepted: IntercityBookingRow[];
}

export interface IntercityAcceptResult {
  accepted: string[];
  rejected: Array<{ bookingId: string; reason: string }>;
  remaining: IntercityCapacity;
}

// ── Yard load / unload worklist ──────────────────────────────────────────────
// Per-booking journey along the train's corridor: every booking boards at its
// origin yard and alights at its destination yard, confirmed by the yard
// operator while the train's latest checkpoint is at that yard.

export interface YardWorkBookingRow {
  id: string;
  reference: string | null;
  status: string;
  tradeDirection: string;
  isGovernment: boolean;
  customer: string;
  originYardId: string;
  destinationYardId: string;
  origin: string;
  destination: string;
  loadedAt: string | null;
  arrivedAt: string | null;
  canLoad: boolean;
  canUnload: boolean;
}

export interface YardWorkYard {
  yardId: string;
  yard: string;
  toLoad: YardWorkBookingRow[];
  toUnload: YardWorkBookingRow[];
}

export interface YardWorkResult {
  scheduleId: string;
  scheduleStatus: string;
  trainAtYardId: string | null;
  yards: YardWorkYard[];
}

export interface BookingLoadResult {
  bookingId: string;
  status: string;
  loadedAt: string;
}

export interface BookingUnloadResult {
  bookingId: string;
  /** 'ARRIVED' for import/export, 'COMPLETED' for intercity. */
  status: string;
  arrivedAt: string;
}

/** An intercity booking's ride-along state, with both ends' facility status. */
export interface IntercityRideAlongRow {
  bookingId: string;
  reference: string | null;
  status: string;
  freightType: string | null;
  weightTons: number | null;
  loadedAt: string | null;
  arrivedAt: string | null;
  customer: string | null;
  trainScheduleId: string | null;
  trainNumber: string | null;
  scheduleStatus: string | null;
  originYardId: string | null;
  origin: string | null;
  originHasFacility: boolean | null;
  destinationYardId: string | null;
  destination: string | null;
  destinationHasFacility: boolean | null;
  /** Yard the train was last recorded at — cargo can only be worked there. */
  trainAtYardId: string | null;
  grnNumber: string | null;
}
