import type { BaseEntity } from "../common";
import { ClearanceNextAction, ContractDocPhase, IClearanceMilestone } from "./contracts";

export * from "./dropdown_settings";
export * from "./file_upload_settings";
export * from "./overview";
export * from "./etrade";
export * from "./contracts";
export * from "./clearance-files.catalog";
export * from "./ethiopian-regions.catalog";
export * from "./notifications";
export * from "./booking-window-ws";
export * from "./support-chat";

export enum TradeDirection {
  IMPORT = "IMPORT",
  EXPORT = "EXPORT",
  BOTH = "BOTH",
}

export enum PriorityType {
  USD_PAYER = "USD_PAYER",
  RAIL_AND_FORWARDING = "RAIL_AND_FORWARDING",
  GOVERNMENT_ACCOUNT = "GOVERNMENT_ACCOUNT",
  HIGH_VOLUME_SHIPMENT = "HIGH_VOLUME_SHIPMENT",
}

/** Bonus applied to government bookings so they outrank commercial priority. */
export const GOVERNMENT_PRIORITY_BONUS = 50_000;

export interface GovernmentBookingFields {
  isGovernment: boolean;
  governmentInstitution?: string | null;
}

export enum ExceededAction {
  WARNING_ONLY = "WARNING_ONLY",
  HARD_BLOCK = "HARD_BLOCK",
}

export enum CalculationMethod {
  PER_TON = "PER_TON",
  FLAT_FEE = "FLAT_FEE",
  PERCENTAGE = "PERCENTAGE",
}

export enum FreightType {
  Container = "CONTAINER",
  Bulk = "BULK",
}

/**
 * Distinguishes a normal one-time booking from a general contract — an umbrella
 * commitment that is signed and paid once, then drawn down by many orders over
 * its period. Stored on the booking row.
 */
export enum BookingType {
  OneTime = "ONE_TIME",
  GeneralContract = "GENERAL_CONTRACT",
}

/**
 * How a cargo type's quantity is measured. Bulk cargo is weighed in tons,
 * break-bulk is counted per item. Containerised freight is always counted by
 * container and carries no unit-of-measure.
 */
export enum CargoUnitOfMeasure {
  PerTon = "PER_TON",
  PerItem = "PER_ITEM",
}

export enum BookingStatus {
  Draft = "DRAFT",
  Submitted = "SUBMITTED",
  /** Backoffice re-priced an approved request — customer must reconfirm. */
  PriceChangedPendingConfirm = "PRICE_CHANGED_PENDING_CONFIRM",
  ChangesRequested = "CHANGES_REQUESTED",
  PendingApproval = "PENDING_APPROVAL",
  ApprovedPendingSignature = "APPROVED_PENDING_SIGNATURE",
  Approved = "APPROVED",
  ReadyForAssignment = "READY_FOR_ASSIGNMENT",
  WagonAssigned = "WAGON_ASSIGNED",
  Invoiced = "INVOICED",
  ContractReady = "CONTRACT_READY",
  SignedCustomer = "SIGNED_CUSTOMER",
  FullyExecuted = "FULLY_EXECUTED",
  PnrGenerated = "PNR_GENERATED",
  PaymentVerificationInProgress = "PAYMENT_VERIFICATION_IN_PROGRESS",
  /** Selected in a batch and notified to pay within the pay window. */
  SelectedForBatch = "SELECTED_FOR_BATCH",
  /** @deprecated Use SelectedForBatch */
  AwaitingPayment = "SELECTED_FOR_BATCH",
  /** Missed the 1h pay window — recoverable via move/cancel (no re-approval). */
  Expired = "EXPIRED",
  Paid = "PAID",
  InTransit = "IN_TRANSIT",
  /** Unloaded at the booking's own destination yard (may precede the train's final arrival). */
  Arrived = "ARRIVED",
  Completed = "COMPLETED",
  Delivered = "DELIVERED",
  Rejected = "REJECTED",
  Cancelled = "CANCELLED",
  PendingConsolidation = "PENDING_CONSOLIDATION",
  Consolidated = "CONSOLIDATED",
  // Post counter-sign document-clearance gate (GL workflow).
  AwaitingDocuments = "AWAITING_DOCUMENTS",
  DocumentsUnderReview = "DOCUMENTS_UNDER_REVIEW",
  ClearanceReady = "CLEARANCE_READY",
  /** Road (truck) drawdown: waits for truck dispatch after Marketing accepts. */
  RoadDispatchPending = "ROAD_DISPATCH_PENDING",
  TruckAssigned = "TRUCK_ASSIGNED",
  OperationRequested = "OPERATION_REQUESTED",
  /** Operations review gate: customer submitted a schedule day for capacity/doc/route review. */
  OperationRequestPending = "OPERATION_REQUEST_PENDING",
  OperationChangesRequested = "OPERATION_CHANGES_REQUESTED",
  OperationPricePendingConfirm = "OPERATION_PRICE_PENDING_CONFIRM",
  /** General contract: paid umbrella contract that is accepting drawdown orders. */
  ContractActive = "CONTRACT_ACTIVE",
  /** General contract: closed because its quantity was exhausted (or period elapsed). */
  ContractClosed = "CONTRACT_CLOSED",
}

export enum ConsignmentStatus {
  Pending = "PENDING",
  Loaded = "LOADED",
  InTransit = "IN_TRANSIT",
  AtDestination = "AT_DESTINATION",
  Delivered = "DELIVERED",
  Returned = "RETURNED",
}

export enum TrainStatus {
  Available = "AVAILABLE",
  Scheduled = "SCHEDULED",
  InService = "IN_SERVICE",
  UnderMaintenance = "UNDER_MAINTENANCE",
  OutOfService = "OUT_OF_SERVICE",
  /** Parked indefinitely by staff; only allowed with no active schedule. */
  Deactivated = "DEACTIVATED",
}

export enum CargoType {
  Container = "CONTAINER",
  BulkLiquid = "BULK_LIQUID",
  BulkDry = "BULK_DRY",
  General = "GENERAL",
  Refrigerated = "REFRIGERATED",
  Hazardous = "HAZARDOUS",
}

export enum PaymentStatus {
  Pending = "PENDING",
  Paid = "PAID",
  Overdue = "OVERDUE",
  Cancelled = "CANCELLED",
  Refunded = "REFUNDED",
}

export enum InvoiceStatus {
  Draft = "DRAFT",
  /** Issued and awaiting payment (alias of PENDING for fee invoices). */
  Issued = "ISSUED",
  Pending = "PENDING",
  /** Some, but not all, of the balance has been settled. */
  PartiallyPaid = "PARTIALLY_PAID",
  Paid = "PAID",
  Overdue = "OVERDUE",
  Cancelled = "CANCELLED",
  Refunded = "REFUNDED",
  /** Pay window closed before settlement; terminal, cannot be paid. */
  Expired = "EXPIRED",
}

/** Originating subsystem an invoice bills for; namespaces invoice events. */
export enum InvoiceSource {
  Booking = "booking",
  Warehouse = "warehouse",
  Demurrage = "demurrage",
  FirstMile = "firstmile",
  LastMile = "lastmile",
  /** Customs clearance service fee — billed on the booking invoice with the freight. */
  Clearance = "clearance"
}

export enum SchedulingStatus {
  NotScheduled = "NOT_SCHEDULED",
  Holding = "HOLDING",
  Eligible = "ELIGIBLE",
  Scheduled = "SCHEDULED",
  Dispatched = "DISPATCHED",
  /** Paid, but no wagon of the required type was free — held in the day pool for manual placement. */
  WaitingForWagon = "WAITING_FOR_WAGON",
}

export enum TrainScheduleStatus {
  Draft = "DRAFT",
  Scheduled = "SCHEDULED",
  Dispatched = "DISPATCHED",
  Arrived = "ARRIVED",
  Cancelled = "CANCELLED",
}

/** Whether a schedule is still accepting / holding bookings (orthogonal to its operational status). */
export enum ScheduleBookingWindow {
  Open = "OPEN",
  Full = "FULL",
  Closed = "CLOSED",
}

export enum AllocationLoadType {
  Container = "CONTAINER",
  Bulk = "BULK",
}

export enum AllocationStatus {
  Planned = "PLANNED",
  Reserved = "RESERVED",
  Loaded = "LOADED",
  Departed = "DEPARTED",
}

export enum TrainSetWagonStatus {
  Planned = "PLANNED",
  Reserved = "RESERVED",
  Loaded = "LOADED",
  Departed = "DEPARTED",
}

export enum LoadingStatus {
  Loaded = "LOADED",
  Unloaded = "UNLOADED",
}

export enum WagonStatus {
  Available = "AVAILABLE",
  Assigned = "ASSIGNED",
  ImportReady = "IMPORT_READY",
  ExportReady = "EXPORT_READY",
  Maintenance = "MAINTENANCE",
  /** Formerly RETIRED — wagons pulled from circulation. */
  Detained = "DETAINED",
}

export enum WagonReadiness {
  ImportReady = "IMPORT_READY",
  ExportReady = "EXPORT_READY",
}

/**
 * Frozen record of one wagon slot's allocation at the moment a schedule leaves
 * DRAFT/SCHEDULED (dispatch / arrive / cancel). Captured once into
 * `train_schedules.wagon_allocation_snapshot` so the historical wagon plan
 * survives later re-pinning of the same physical wagons onto other trains.
 */
export interface WagonAllocationSnapshotSlot {
  sequenceNo: number;
  trainSetWagonId: string;
  physicalWagonId: string | null;
  physicalWagonNumber: string | null;
  wagonTypeId: string | null;
  wagonTypeCode: string | null;
  /** Wagon slot status (RESERVED/LOADED/…) at capture time. */
  slotStatus: string | null;
  boardYardId: string | null;
  alightYardId: string | null;
  allocations: WagonAllocationSnapshotAllocation[];
}

export interface WagonAllocationSnapshotAllocation {
  bookingId: string;
  bookingReference: string | null;
  allocatedWeightTons: number;
  loadType: string | null;
  containerNumbers: string[];
}

/** Whole-schedule frozen wagon plan written at a terminal/transit transition. */
export interface WagonAllocationSnapshot {
  /** Schedule status the snapshot was captured at (DISPATCHED/ARRIVED/CANCELLED). */
  capturedStatus: string;
  capturedAt: string;
  slots: WagonAllocationSnapshotSlot[];
}

export type ScheduleTradeDirection = "IMPORT" | "EXPORT" | "DOMESTIC";

/**
 * The only two countries on the EDR line. Yard `country` is constrained to
 * these values; route trade direction is derived from the origin/destination
 * yard countries (ET→DJ = EXPORT, DJ→ET = IMPORT, same-country = DOMESTIC,
 * shown as "Intercity" in UIs and currently disabled for scheduling/contracts).
 */
export enum YardCountry {
  ETHIOPIA = "Ethiopia",
  DJIBOUTI = "Djibouti",
}

/** UI label for a schedule/route trade direction (DOMESTIC displays as Intercity). */
export const TRADE_DIRECTION_LABELS: Record<ScheduleTradeDirection, string> = {
  IMPORT: "Import",
  EXPORT: "Export",
  DOMESTIC: "Intercity",
};

export enum TrainCheckpointKind {
  Departed = "DEPARTED",
  Passed = "PASSED",
  Arrived = "ARRIVED",
}

export interface ITrainCheckpointEvent extends BaseEntity {
  trainScheduleId: string;
  yardId: string;
  sequenceNo: number;
  kind: TrainCheckpointKind;
  occurredAt: string;
  note?: string | null;
  recordedByUserId?: string | null;
}

/** Why a physical wagon moved between yards — the wagon_movements ledger kind. */
export enum WagonMovementKind {
  /** Carried a booking's cargo for its leg (board yard → alight yard). */
  Loaded = "LOADED",
  /** Rode a train empty to reposition for later use. */
  EmptyReposition = "EMPTY_REPOSITION",
  /** Staff manually corrected/updated the wagon's yard. */
  Manual = "MANUAL",
}

export interface IWagonMovement extends BaseEntity {
  wagonId: string;
  fromYardId?: string | null;
  toYardId: string;
  trainScheduleId?: string | null;
  bookingId?: string | null;
  kind: WagonMovementKind;
  movedByUserId?: string | null;
  /** The transfer request this move fulfilled, when it came from one. */
  transferRequestId?: string | null;
  occurredAt: string;
  note?: string | null;
}

/**
 * Lifecycle of a two-person wagon-transfer request. A requester asks for N
 * wagons of a type to move from one yard to another (count only, no specific
 * wagons); OCC staff later pick the physical wagons and execute the move.
 */
export enum WagonTransferRequestStatus {
  /** Awaiting OCC fulfilment — nothing moved yet. */
  Pending = "PENDING",
  /**
   * Some of the asked-for wagons have moved and the request is still open. OCC
   * keeps sending more, any number at any time, until the full count is met.
   */
  PartiallyFulfilled = "PARTIALLY_FULFILLED",
  /** The full requested count has moved. */
  Fulfilled = "FULFILLED",
  /**
   * OCC ended the request with fewer wagons than asked for — the source yard
   * has no more to give. The shortfall must be requested from another yard.
   */
  ClosedShort = "CLOSED_SHORT",
  /** Requester or OCC withdrew it before anything moved. */
  Cancelled = "CANCELLED",
}

/** Statuses where OCC can still move wagons against the request. */
export const OPEN_WAGON_TRANSFER_STATUSES: WagonTransferRequestStatus[] = [
  WagonTransferRequestStatus.Pending,
  WagonTransferRequestStatus.PartiallyFulfilled,
];

export interface IWagonTransferRequest extends BaseEntity {
  fromYardId: string;
  toYardId: string;
  wagonTypeId: string;
  /** How many wagons of `wagonTypeId` to move out of `fromYardId`. */
  quantity: number;
  /** How many have actually moved so far — 0 until the first transfer. */
  fulfilledQuantity: number;
  status: WagonTransferRequestStatus;
  requestedByUserId?: string | null;
  fulfilledByUserId?: string | null;
  fulfilledAt?: string | null;
  /** Set when OCC ended the request short of the asked-for count. */
  closedShortAt?: string | null;
  /** Why the wagons are needed — required for new requests, shown on the OCC queue. */
  reason?: string | null;
  note?: string | null;
}

export enum BulkPricingUnit {
  PerWagon = "PER_WAGON",
  PerTon = "PER_TON",
  PerItem = "PER_ITEM",
}

export type CustomerStatus = "Active" | "Pending" | "Inactive";
export type CustomerType = "Importer" | "Exporter" | "Supplier";

export interface ICustomer extends BaseEntity {
  userId: string;
  firstName: string;
  lastName: string;
  email: string;
  phone: string;
  companyName: string;
  companyEmail: string;
  companyPhone: string;
  companyLocation: string;
  companyAddress: string;
  contactPersonName: string;
  contactPersonPhone: string;
  tinNumber: string;
  vatNumber?: string | null;
  fanNumber: string;
  generalManagerName: string;
  generalManagerEmail: string;
  generalManagerPhone: string;
  poaName?: string | null;
  poaPhone?: string | null;
  poaAddress?: string | null;
  poaEmail?: string | null;
  poaLocation?: string | null;
  notes?: string | null;
}

export interface CreateCustomerDto {
  userId: string;
  firstName: string;
  lastName: string;
  email: string;
  phone: string;
  companyName: string;
  companyEmail: string;
  companyPhone: string;
  companyLocation: string;
  companyAddress: string;
  contactPersonName: string;
  contactPersonPhone: string;
  tinNumber: string;
  vatNumber: string;
  fanNumber: string;
  generalManagerName: string;
  generalManagerEmail: string;
  generalManagerPhone: string;
  poaName?: string;
  poaPhone?: string;
  poaAddress?: string;
  poaEmail?: string;
  poaLocation?: string;
  notes?: string;
}

export type UpdateCustomerDto = Partial<CreateCustomerDto>;

export interface ITrain extends BaseEntity {
  code: string;
  capacityTons: number;
  status: TrainStatus;
  notes?: string;
}

export interface ITrackingEvent extends BaseEntity {
  consignmentId: string;
  location: string;
  status: ConsignmentStatus;
  occurredAt: string;
  description?: string;
}

export interface IConsignment extends BaseEntity {
  bookingId: string;
  trackingNumber: string;
  cargoType: CargoType;
  weightKg: number;
  status: ConsignmentStatus;
  originStation: string;
  destinationStation: string;
}

/** One station along the train's corridor (origin → milestones → destination). */
export interface ITrackingStation {
  sequenceNo: number;
  yardId: string;
  label: string;
  code: string;
}

/** A logged checkpoint as the train passes a station. */
export interface ITrackingCheckpoint {
  id: string;
  sequenceNo: number;
  yardId: string;
  label: string | null;
  kind: TrainCheckpointKind;
  occurredAt: string;
  note: string | null;
}

/**
 * Customer-facing shipment tracking payload for a single booking, derived from
 * the train schedule the booking is assigned to and the live checkpoint log.
 *
 * `hasSchedule` is false when the booking has not been assigned to a train yet
 * (still pre-dispatch) — the UI shows a "not on the rails yet" state.
 */
export interface IBookingTracking {
  bookingId: string;
  bookingReference: string;
  hasSchedule: boolean;
  scheduleId: string | null;
  trainNumber: string | null;
  /** Operational status of the assigned schedule (DRAFT/SCHEDULED/DISPATCHED/ARRIVED). */
  scheduleStatus: TrainScheduleStatus | null;
  direction: string | null;
  origin: string | null;
  destination: string | null;
  /** Ordered stations forming the corridor. */
  stations: ITrackingStation[];
  /** Logged checkpoints, ordered by sequence then time. */
  checkpoints: ITrackingCheckpoint[];
  /** Highest reached station sequence (−1 = not departed). */
  currentSequenceNo: number;
  actualDepartureAt: string | null;
  actualArrivalAt: string | null;
  /** Planned departure/arrival from the schedule, used as ETA hints. */
  scheduledDepartureAt: string | null;
  scheduledArrivalAt: string | null;
  // ── Per-booking journey (segment corridor bookings) ─────────────────────
  /** The booking's own status — the journey is per booking, not per train. */
  bookingStatus?: string | null;
  /** The leg this booking rides: its own origin/destination yards. */
  bookingOriginYardId?: string | null;
  bookingDestinationYardId?: string | null;
  /** Operator-confirmed load at the booking's origin yard (per-booking dispatch). */
  loadedAt?: string | null;
  /** Operator-confirmed unload at the booking's destination yard (per-booking arrival). */
  arrivedAt?: string | null;
}

export interface IYard extends BaseEntity {
  code: string;
  label: string;
  country: `${YardCountry}`;
  isActive: boolean;
  displayOrder: number;
}

/** One container number loaded onto a customer self-haul truck. */
export interface ICustomerTruckContainer {
  id: string;
  containerNumber: string;
}

/** A customer self-haul truck on a booking, carrying 1–2 containers. */
export interface ICustomerTruck {
  id: string;
  bookingId: string;
  plateNumber: string;
  driverName: string;
  truckType: string;
  assignedAt: string;
  arrivedAt?: string | null;
  departedAt?: string | null;
  containers?: ICustomerTruckContainer[];
}

/** Payload to add a customer self-haul truck (1–2 container numbers). */
export interface AddCustomerTruckPayload {
  truckPlateNumber: string;
  driverName: string;
  truckType: string;
  containerNumbers: string[];
}

export interface IBooking extends BaseEntity {
  reference: string;
  customerId: string;
  /** The contract this booking was created under (Path A / Path B). */
  contractId?: string | null;
  /** Human-readable reference of the parent contract, joined onto list rows. */
  contractReference?: string | null;
  trainId?: string | null;
  status: BookingStatus;
  /**
   * Operational status of the assigned train (DRAFT/SCHEDULED/DISPATCHED/
   * ARRIVED), joined on booking detail. The booking status stays IN_TRANSIT
   * from dispatch until delivery, so the portal stepper reads arrival from
   * here.
   */
  trainScheduleStatus?: TrainScheduleStatus | string | null;
  /** ONE_TIME for normal bookings; GENERAL_CONTRACT for umbrella contracts. */
  bookingType?: BookingType;
  /**
   * Denormalized kind of the parent contract (ONE_TIME | GENERAL). Drawdown
   * bookings under a GENERAL contract keep bookingType = ONE_TIME, so this is
   * the field UIs must read to label a booking "General".
   */
  contractKind?: `${import("./contracts").ContractKind}` | null;
  /** General contracts only: when ordering closes (null until active / for one-time). */
  expiresAt?: string | null;
  /** Null for general contracts at creation — the date is chosen per order. */
  scheduledDate: string;
  totalAmount: number;
  /** Staff-adjusted total that overrides totalAmount for the customer, if set. */
  adjustedTotalAmount?: number | null;
  adjustedByStaffId?: string | null;
  adjustedAt?: string | null;
  adjustmentReason?: string | null;
  /**
   * Contract validity window set by the backoffice at the accept step. Valid
   * from contractValidFrom through contractValidUntil (validFrom + N days).
   */
  contractValidityDays?: number | null;
  contractValidFrom?: string | null;
  contractValidUntil?: string | null;
  paymentStatus: PaymentStatus;

  shippingLineId?: string | null;
  serviceTypeId: string;

  contractType: "NEW" | "RENEWAL";
  previousContractId?: string | null;
  serviceType: "RAIL_ONLY" | "RAIL_AND_FORWARDING";

  firstMileEnabled: boolean;
  firstMilePickupAddress?: string | null;
  firstMilePickupLat?: number | null;
  firstMilePickupLng?: number | null;
  lastMileEnabled: boolean;
  lastMileDeliveryAddress?: string | null;
  lastMileDeliveryLat?: number | null;
  lastMileDeliveryLng?: number | null;
  customerTruckPlateNumber?: string | null;
  customerTruckDriverName?: string | null;
  customerTruckType?: string | null;
  customerTruckContainerNumber?: string | null;
  customerTruckAssignedAt?: string | null;
  customerTruckArrivedAt?: string | null;
  /** A handover has been generated for this booking and is awaiting the customer's signature. */
  handoverAwaitingSignature?: boolean;

  customsClearingEnabled?: boolean;
  // (multi-truck self-haul lives in ICustomerTruck[], fetched via the
  //  /customer-trucks endpoint; the fields above are the booking-level flag.)
  customsClearingAgent?: string | null;

  equipmentReturn: "WITH_RETURN" | "WITHOUT_RETURN";
  originYard?: IYard | null;
  destinationYard?: IYard | null;
  cargoTotalWeightVgm: number;

  freightType: FreightType;
  freightSubtype?: string | null;

  isHazardous: boolean;
  isRefrigerated: boolean;
  /** Bulk-only hazardous amount in the cargo's unit (tons/items); 0 otherwise. */
  bulkHazardousQuantity?: number;
  /** Bulk-only refrigerated amount in the cargo's unit (tons/items); 0 otherwise. */
  bulkReeferQuantity?: number;

  tradeDirection: "IMPORT" | "EXPORT";
  paymentCurrency: string;
  consolidationPartnerId?: string | null;

  startDate?: string | null;
  endDate?: string | null;
  financialTerms?: string | null;

  /** When the batch engine picked this booking and opened the pay window. */
  selectedForBatchAt?: string | null;
  /** End of the pay window once the booking is SELECTED_FOR_BATCH. */
  paymentDeadline?: string | null;

  containers?: Array<{ type: string; qty: number; vgm: number }> | null;

  /** Physical container numbers on this booking (from booking container units),
   *  surfaced for the customer truck-assignment container picker. */
  containerNumbers?: string[] | null;

  versionNumber: number;
  priorityScore: number;

  approvedByStaffId?: string | null;
  approvedByStaffAt?: string | null;
  signedByDirectorId?: string | null;
  signedByDirectorAt?: string | null;
  signedByCeoId?: string | null;
  signedByCeoAt?: string | null;

  files?: Array<{
    id: string;
    code: string;
    name: string;
    url: string;
    mimeType: string;
    size: number;
    resourceId: string;
    resource: string;
    signedUrl?: string | null;
  }>;

  latestChangeRequestNote?: string | null;
  contractSummary?: string | null;
  pricingBreakdown?: PricingBreakdown | null;

  /**
   * The customer paid a partial batch offer and this booking was reduced to
   * the offered part. On a ONE_TIME contract this frees the single-booking
   * slot for the remainder: the next booking must take the whole leftover
   * quantity (the contract kind itself never changes).
   */
  isSplit?: boolean;
  /** What this booking carried before it was reduced by a split (bulk tons / units per size). */
  preSplitQuantities?: { bulkTons?: number; bySize?: Record<string, number> } | null;
}

export interface PricingBreakdownLineItem {
  code: string;
  /** Computed line total (unitAmount × quantity). */
  amount: number;
  /** Price for a single unit of this charge (e.g. one 20ft container, one ton). */
  unitAmount?: number;
  /** Unit the rate is charged per: PER_CONTAINER | PER_TON | PER_WAGON | PER_KM | FLAT. */
  unit?: string;
  /** How many units this charge applies to (containers, tons, wagons; 1 for FLAT). */
  quantity?: number;
  currency: string;
  description: string;
}

export interface PricingBreakdown {
  currency: string;
  lineItems: PricingBreakdownLineItem[];
  generatedAt: string;
  totalAmount: number;
}

// ── Document clearance (post counter-sign GL workflow) ──────────────────────

export type DocumentReviewStatus = "PENDING" | "APPROVED" | "QUERIED";

/** One row of the clearance document grid (a required doc + its file + review). */
export interface ClearanceDocument {
  fileKey: string;
  label: string;
  required: boolean;
  /** Who supplies this document: the customer, or Global Logistics staff. */
  uploadedBy: "customer" | "gl";
  settingCode: string;
  file: { id: string; name: string; url: string } | null;
  reviewStatus: DocumentReviewStatus | null;
  note: string | null;
}

/** The clearance view for a booking, driving both portals' clearance UI. */
export interface ClearanceView {
  status: string;
  includesCustoms: boolean;
  inputCode: string | null;
  outputCode: string | null;
  documents: ClearanceDocument[];
  /** True once every required customer document is APPROVED (the 100% gate). */
  allApproved: boolean;
  /** Phased clearance (GENERAL + customs per-booking). */
  phase?: ContractDocPhase | null;
  milestones?: IClearanceMilestone[];
  nextAction?: ClearanceNextAction | null;
  dutyRequired?: boolean | null;
  roHold?: boolean;
  roHoldReason?: string | null;
  vesselDepartureDate?: string | null;
  /** Import DO: vessel arrival + DO collection dates, recorded by GL Djibouti. */
  vesselArrivalDate?: string | null;
  doCollectedDate?: string | null;
  roAmendmentRequestedAt?: string | null;
  /** Boundary milestone complete — customer may proceed to operations. */
  operationReady?: boolean;
  preClearanceFinalized?: boolean;
  dutyAdvice?: {
    amount: number;
    currency: string;
    declarationSerial?: string | null;
    noticeFile?: { id: string; name: string; url: string } | null;
  } | null;
  /** Phased customs uploads (IM4, DO, transit permit, etc.) with friendly labels. */
  workflowFiles?: import("./clearance-files.catalog").ClearanceWorkflowFile[];
  /** Import post-allocation T1 transit document state (null until wagon allocation). */
  t1?: import("./contracts").ClearanceT1State | null;
  /** Train link state for the booking (both directions). */
  train?: import("./contracts").ClearanceTrainState | null;
  gatepassGranted?: boolean;
  gatepassAt?: string | null;
  t1Closed?: boolean;
  t1ClosedAt?: string | null;
  offloaded?: boolean;
  /** GL Djibouti post-offload final invoice (export). */
  finalInvoice?: import("./contracts").ClearanceFinalInvoiceSummary | null;
  /** Customs risk level assigned by GL ET (import; visible to the customer). */
  riskLevel?: string | null;
  riskAssignedAt?: string | null;
  /** Every risk decision, oldest first; the last entry is the current level. */
  riskHistory?: import("./contracts").RiskAssignmentRecord[];
  /** Post-arrival additional duty/tax round (import). */
  secondDuty?: import("./contracts").ClearanceSecondDuty | null;
  importReleaseGranted?: boolean;
}

/** Company an invoice is billed to (minimal projection). */
export interface IInvoiceCompany {
  id: string;
  name: string;
}

/** Company profile (importer/exporter/forwarder/…) an invoice is billed to. */
export interface IInvoiceCompanyProfile {
  id: string;
  type: string;
  reference: string | null;
}

/** A single billed line on an invoice. */
export interface IInvoiceLine extends BaseEntity {
  invoiceId: string;
  chargeType: string;
  description?: string | null;
  /** Units billed (container count, wagon count, tons, …); defaults to 1. */
  quantity: number;
  /** Price per unit; `amount` is normally `quantity * unitRate`. */
  unitRate: number;
  amount: number;
  currency: string;
  metadata?: Record<string, unknown> | null;
}

export interface IInvoice extends BaseEntity {
  invoiceNumber: string;
  /** Customer (company) the invoice is billed to. */
  companyId: string;
  company?: IInvoiceCompany;
  /** Specific company profile billed. */
  companyProfileId: string;
  companyProfile?: IInvoiceCompanyProfile;
  totalAmount: number;
  /** Cumulative amount settled so far (supports partial payment). */
  paidAmount: number;
  /** Outstanding balance = totalAmount - paidAmount (0 once fully paid). */
  balanceAmount: number;
  currency: string;
  status: InvoiceStatus;
  /** Originating subsystem: booking / warehouse / demurrage. */
  source: InvoiceSource;
  /** Identifier of the source record (e.g. booking id). */
  sourceId: string;
  /** What the invoice bills for (e.g. PREPAID). */
  type: string;
  /** Set when issued; null while DRAFT. */
  issuedAt?: string | null;
  /** Gateway payment that settled the invoice, once paid. */
  paymentId?: string | null;
  dueAt: string;
  /** Present on invoice-detail reads. */
  lines?: IInvoiceLine[];
}

// â”€â”€ Reference Data (booking form catalog) â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€

export interface BookingReferenceYard {
  id: string;
  name: string;
  code: string;
  country: string;
}

export interface BookingReferenceContainerType {
  id: string;
  name: string;
  code: string;
  is_reefer: boolean;
}

export interface BookingReferenceContainerSizeGroup {
  size: string;
  types: BookingReferenceContainerType[];
}

export interface BookingReferenceService {
  id: string;
  name: string;
  code: string;
  serviceName: string;
  description?: string | null | undefined;
  canBeBookedAlone: boolean;
  includesFirstMile: boolean;
  includesLastMile: boolean;
  includesCustoms: boolean;
  isActive: boolean;
  displayOrder: number;
  createdAt: string;
  updatedAt: string;
  deletedAt?: string | null | undefined;
}

export interface BookingReferenceShippingLine {
  id: string;
  name: string;
  code: string;
}

export interface BookingReferenceCargoTypeChild {
  id: string;
  name: string;
  code: string;
  /** How this cargo is measured (PER_TON / PER_ITEM); null when unset. */
  unit_of_measure?: CargoUnitOfMeasure | null;
}

export interface BookingReferenceCargoTypeGroup {
  id: string;
  name: string;
  code: string;
  children?: BookingReferenceCargoTypeChild[];
}

export interface BookingReferenceData {
  yard: BookingReferenceYard[];
  containers: BookingReferenceContainerSizeGroup[];
  service: BookingReferenceService[];
  shipping_line: BookingReferenceShippingLine[];
  cargo_type: BookingReferenceCargoTypeGroup[];
}

// ── Train Scheduling (bookable schedules) ──────────────────────────────────────

export interface BookableSchedulesQuery {
  originYardId?: string;
  destinationYardId?: string;
}

export interface AvailableDaysQuery {
  originYardId?: string;
  destinationYardId?: string;
}

/**
 * Cargo-aware availability query: beyond the route yards it carries the cargo
 * sizing so the server only returns days where a train has remaining capacity
 * AND enough matching-type wagons. Response reuses {@link AvailableDaysResponse}.
 */
export interface AvailableDaysForCargoQuery {
  originYardId?: string;
  destinationYardId?: string;
  freightType: "CONTAINER" | "BULK";
  /** Bulk cargo type code (e.g. "COFFEE"); ignored for container freight. */
  cargoTypeCode?: string;
  /** Bulk cargo type id — preferred over code for the wagon-type gate. */
  cargoTypeId?: string;
  /** Total bulk weight in tons. */
  totalWeightTons?: number;
  /** Container lines (size + quantity) for container freight. */
  containers?: { containerSize: string; quantity: number }[];
  /** Container type ids — enables the exact wagon-type compatibility gate. */
  containerTypeIds?: string[];
}

/**
 * Day-level booking pool: the EAT calendar days that have at least one OPEN
 * departure on a route. `days` are `yyyy-MM-dd` strings, e.g. `["2026-06-20"]`.
 * No capacity is exposed — customers pick a day, not a train.
 */
export interface AvailableDaysResponse {
  days: string[];
}

/**
 * Advisory free-wagon count for a shipment day a customer is considering — a
 * planning hint, never enforced (the batch engine and the export gate are the
 * real authorities). `trainsForDay` is false when no departure carries the leg.
 *
 * - EXPORT: `fits` = a single open train that day can carry the WHOLE booking
 *   (export never splits); `freeWagons` = the largest single-train leftover.
 * - IMPORT/DOMESTIC: `freeWagons` = TOTAL room across the day's trains for the
 *   booking's wagon type; `fits` = that total covers the booking. The batch may
 *   still split the booking or defer a remainder to a later window.
 */
export interface DayAvailabilityResponse {
  fits: boolean;
  freeWagons: number;
  trainsForDay: boolean;
}

export interface BookableScheduleLocomotive {
  id: string;
  code: string;
  name: string | null;
  readiness: string | null;
}

export interface BookableScheduleItem {
  id: string;
  scheduleDate: string;
  trainNumber: string | null;
  routeName: string | null;
  origin: string | null;
  destination: string | null;
  locomotive: BookableScheduleLocomotive | null;
  wagonCount: number;
  totalWeightTons: number;
  totalLengthMeters: number;
  bookingsCount: number;
  freightType: FreightType | "MIXED" | null;
  status: TrainScheduleStatus;
  bookingWindowStatus: ScheduleBookingWindow;
  maxWagons: number;
  remainingWagons: number;
}

// ── DTOs ───────────────────────────────────────────────────────────────────────

export interface CreateBookingContainerDto {
  containerTypeId: string;
  quantity: number;
  vgmPerUnitTons: number;
  /** How many of this line's containers are hazardous (0..quantity). */
  hazardousQuantity?: number;
  /** How many of this line's containers are refrigerated (0..quantity). */
  reeferQuantity?: number;
}

/** A contracted route+quantity line for a GENERAL contract. */
/**
 * A contracted route (lane) of a general contract — a pure origin→destination
 * pair. Routes carry NO quantity; the contract draws from a single shared pool.
 */
export interface CreateContractRouteDto {
  originYardId: string;
  destinationYardId: string;
  /** Road distance (km) for this route; used to bill road orders. */
  km?: number | undefined;
}

export interface CreateBookingDto {
  freightShapeValidation?: boolean | undefined;
  reference?: string | undefined;
  isGovernment?: boolean | undefined;
  governmentInstitution?: string | undefined;
  companyId?: string | undefined;
  trainId?: string | undefined;
  trainScheduleId?: string | undefined;
  /** Binding shipment day — set at the operation-request step, not at creation. */
  scheduledDate?: string | undefined;
  /** Non-binding shipment-date estimate captured in the booking wizard. */
  estimatedShipmentDate?: string | undefined;
  /** Defaults to ONE_TIME. GENERAL_CONTRACT creates an umbrella contract. */
  bookingType?: BookingType | undefined;
  contractType: string;
  previousContractId?: string | undefined;
  serviceTypeId: string;
  firstMilePickupAddress?: string | undefined;
  firstMilePickupLat?: number | undefined;
  firstMilePickupLng?: number | undefined;
  lastMileDeliveryAddress?: string | undefined;
  lastMileDeliveryLat?: number | undefined;
  lastMileDeliveryLng?: number | undefined;
  customsClearingEnabled?: boolean | undefined;
  customsClearingAgent?: string | undefined;
  equipmentReturn: string;
  originYardId: string;
  destinationYardId: string;
  tradeDirection: string;
  freightType: string;
  cargoTypeId?: string | undefined;
  cargoFreeText?: string | undefined;
  shippingLineId?: string | undefined;
  cargoTotalWeightVgm: number;
  isHazardous?: boolean | undefined;
  /** Booking-level refrigerated flag (bulk freight only; containers derive reefer from the container type). */
  isReefer?: boolean | undefined;
  /** Bulk-only: hazardous amount in the cargo's unit (tons or items), <= cargoTotalWeightVgm. */
  bulkHazardousQuantity?: number | undefined;
  /** Bulk-only: refrigerated amount in the cargo's unit (tons or items), <= cargoTotalWeightVgm. */
  bulkReeferQuantity?: number | undefined;
  paymentCurrency: string;
  pnrCode?: string | undefined;
  startDate?: string | undefined;
  endDate?: string | undefined;
  financialTerms?: string | undefined;
  containers?: CreateBookingContainerDto[];
  /** GENERAL_CONTRACT only: routes the contract reserves quantity across. */
  routes?: CreateContractRouteDto[];
}

// ── General Contracts & Booking Orders ──────────────────────────────────────────

/**
 * A line of contracted quantity. For CONTAINER contracts there is one line per
 * container type (each its own drawdown pool); for BULK/BREAK_BULK a single line
 * with a null containerTypeId carries the total tons/items.
 */
export interface ContractQuantityLine {
  containerTypeId: string | null;
  containerTypeName?: string | null;
  /** PER_TON / PER_ITEM for bulk-style lines; null for container lines. */
  unitOfMeasure?: CargoUnitOfMeasure | null;
  /** Total contracted units on this line (containers, tons, or items). */
  contractedQuantity: number;
  /** Units already drawn down by non-cancelled orders. */
  orderedQuantity: number;
  /** contractedQuantity − orderedQuantity. */
  remainingQuantity: number;
}

/**
 * Customer-facing view of a general contract (a Booking with
 * bookingType = GENERAL_CONTRACT) and its remaining drawdown pool.
 */
export interface IGeneralContract extends IBooking {
  bookingType: BookingType;
  /** When the contract becomes ACTIVE; when ordering closes. Null until active. */
  expiresAt?: string | null;
  /** Per-line contracted / ordered / remaining quantities. */
  quantityLines: ContractQuantityLine[];
}

export interface CreateBookingOrderLineDto {
  /** Null for bulk/break-bulk; the container type id for container contracts. */
  containerTypeId?: string | null;
  quantity: number;
  /** How much of this line is hazardous (≤ quantity). Defaults to 0. */
  hazardousQuantity?: number;
  /** How much of this line is refrigerated (≤ quantity). Defaults to 0. */
  reeferQuantity?: number;
}

/** Per-route contracted / ordered / remaining pool line (multi-route contracts). */
/**
 * A contracted route (lane) of a general contract — a pure origin→destination
 * pair the contract covers. Routes carry NO quantity; the contract draws from a
 * single shared pool ({@link ContractQuantityLine}). An order picks one lane (for
 * scheduling + road billing) and draws from that shared pool.
 */
export interface ContractRouteLine {
  routeLineId: string;
  originYardId: string;
  originYardName?: string | null;
  destinationYardId: string;
  destinationYardName?: string | null;
  /** Road distance (km) for this route; used to bill road orders. Null for rail-only. */
  km?: number | null;
}

export interface CreateBookingOrderDto {
  /** The general contract (booking) this order draws down from. */
  contractBookingId: string;
  /** For multi-route contracts: the route line being drawn from. */
  routeLineId?: string;
  /** The shipment day the customer wants for this order. */
  scheduledDate: string;
  lines: CreateBookingOrderLineDto[];
}

export interface IBookingOrderLine {
  id: string;
  containerTypeId?: string | null;
  containerTypeName?: string | null;
  quantity: number;
  hazardousQuantity?: number;
  reeferQuantity?: number;
}

export interface IBookingOrder {
  id: string;
  reference: string;
  contractBookingId: string;
  /** The child shipment booking spawned for this order (enters scheduling). */
  bookingId?: string | null;
  bookingReference?: string | null;
  companyId?: string | null;
  scheduledDate: string;
  status: BookingStatus;
  schedulingStatus: SchedulingStatus;
  trainScheduleId?: string | null;
  lines: IBookingOrderLine[];
  createdAt: string;
  updatedAt: string;
}
