import { BaseEntity } from '@edr/api-common';
import { Column, Entity, Index, JoinColumn, ManyToOne } from 'typeorm';

import { Booking } from '../../bookings/entities/booking.entity';
import { Cargo } from '../../cargoes/entities/cargoes.entity';
import { Container } from '../../container-management/entities/container.entity';
import { Warehouse } from './warehouse.entity';
import { WarehouseYard } from './warehouse-yard.entity';
import { WarehouseZone } from './warehouse-zone.entity';

// Lifecycle. Supersedes the Batch 1 set
// (ARRIVED_AT_WAREHOUSE / UNDER_INSPECTION / READY_FOR_LOADING) — migrated in place.
// After RECEIVED + inspection (PASSED), the flow branches by booking trade direction:
//   EXPORT/DOMESTIC: STORED → RESERVED → READY_FOR_LOADING → LOADED → DISPATCHED
//   IMPORT:          READY_FOR_PICKUP → DELIVERED (release order + proof of delivery)
export const WAREHOUSE_INVENTORY_STATUSES = [
  'UNLOADED',
  'RECEIVED',
  'STORED',
  'RESERVED',
  'READY_FOR_LOADING',
  'LOADED',
  'DISPATCHED',
  'READY_FOR_PICKUP',
  'DELIVERED',
] as const;
export type WarehouseInventoryStatus = (typeof WAREHOUSE_INVENTORY_STATUSES)[number];

/** Allowed forward transitions for the inventory lifecycle. */
export const WAREHOUSE_INVENTORY_TRANSITIONS: Record<WarehouseInventoryStatus, WarehouseInventoryStatus[]> = {
  // UNLOADED = train-arrival landing state (Batch 8). Not yet stored/inspected.
  // Mirrors RECEIVED so the import flow can store or go straight to pickup after inspection.
  UNLOADED: ['STORED', 'READY_FOR_PICKUP'],
  RECEIVED: ['STORED', 'READY_FOR_PICKUP'],
  STORED: ['RESERVED'],
  RESERVED: ['READY_FOR_LOADING'],
  READY_FOR_LOADING: ['LOADED'],
  LOADED: ['DISPATCHED'],
  DISPATCHED: [],
  // Batch 10: an inspected import item can leave by customer pickup (DELIVERED) or be dispatched
  // out by EDR (DISPATCHED) — kept separate — or be put into storage (STORED) if no one collects
  // it / customs or inspection hold / operator chooses to store.
  READY_FOR_PICKUP: ['DELIVERED', 'STORED', 'DISPATCHED'],
  DELIVERED: [],
};

@Entity({ schema: 'freight', name: 'warehouse_inventory' })
@Index(['warehouseId'])
@Index(['yardId'])
@Index(['zoneId'])
@Index(['bookingId'])
@Index(['cargoId'])
@Index(['containerId'])
@Index(['goodsId'])
@Index(['status'])
export class WarehouseInventory extends BaseEntity {
  @Column({ name: 'warehouse_id', type: 'uuid' })
  warehouseId!: string;

  @ManyToOne(() => Warehouse)
  @JoinColumn({ name: 'warehouse_id' })
  warehouse?: Warehouse;

  @Column({ name: 'yard_id', type: 'uuid' })
  yardId!: string;

  @ManyToOne(() => WarehouseYard)
  @JoinColumn({ name: 'yard_id' })
  yard?: WarehouseYard;

  @Column({ name: 'zone_id', type: 'uuid' })
  zoneId!: string;

  @ManyToOne(() => WarehouseZone)
  @JoinColumn({ name: 'zone_id' })
  zone?: WarehouseZone;

  @Column({ name: 'booking_id', type: 'uuid', nullable: true })
  bookingId?: string | null;

  @ManyToOne(() => Booking, { nullable: true })
  @JoinColumn({ name: 'booking_id' })
  booking?: Booking | null;

  @Column({ name: 'cargo_id', type: 'uuid', nullable: true })
  cargoId?: string | null;

  @ManyToOne(() => Cargo, { nullable: true })
  @JoinColumn({ name: 'cargo_id' })
  cargo?: Cargo | null;

  @Column({ name: 'container_id', type: 'uuid', nullable: true })
  containerId?: string | null;

  @ManyToOne(() => Container, { nullable: true })
  @JoinColumn({ name: 'container_id' })
  container?: Container | null;

  @Column({ name: 'goods_id', type: 'uuid', nullable: true })
  goodsId?: string | null;

  @Column({ name: 'quantity', type: 'numeric', precision: 12, scale: 3, default: 0 })
  quantity!: number;

  @Column({ name: 'weight', type: 'numeric', precision: 14, scale: 3, default: 0 })
  weight!: number;

  @Column({ name: 'volume', type: 'numeric', precision: 12, scale: 3, nullable: true })
  volume?: number | null;

  @Column({ name: 'status', type: 'varchar', length: 32, default: 'RECEIVED' })
  status!: WarehouseInventoryStatus;

  // Batch 4.5: latest inspection outcome (PASSED | FAILED | NEEDS_REVIEW). Null = not yet inspected.
  @Column({ name: 'inspection_status', type: 'varchar', length: 20, nullable: true })
  inspectionStatus?: string | null;

  @Column({ name: 'arrived_at', type: 'timestamptz', nullable: true })
  arrivedAt?: Date | null;

  // Batch 8: when the goods were unloaded off the arrived train (before storage/inspection).
  @Column({ name: 'unloaded_at', type: 'timestamptz', nullable: true })
  unloadedAt?: Date | null;

  @Column({ name: 'stored_at', type: 'timestamptz', nullable: true })
  storedAt?: Date | null;

  @Column({ name: 'reserved_at', type: 'timestamptz', nullable: true })
  reservedAt?: Date | null;

  @Column({ name: 'inspected_at', type: 'timestamptz', nullable: true })
  inspectedAt?: Date | null;

  @Column({ name: 'ready_for_loading_at', type: 'timestamptz', nullable: true })
  readyForLoadingAt?: Date | null;

  @Column({ name: 'loaded_at', type: 'timestamptz', nullable: true })
  loadedAt?: Date | null;

  @Column({ name: 'dispatched_at', type: 'timestamptz', nullable: true })
  dispatchedAt?: Date | null;

  // Batch 5 — demurrage / storage lifecycle timestamps.
  @Column({ name: 'inspection_started_at', type: 'timestamptz', nullable: true })
  inspectionStartedAt?: Date | null;

  @Column({ name: 'inspection_completed_at', type: 'timestamptz', nullable: true })
  inspectionCompletedAt?: Date | null;

  @Column({ name: 'ready_for_pickup_at', type: 'timestamptz', nullable: true })
  readyForPickupAt?: Date | null;

  @Column({ name: 'release_date', type: 'timestamptz', nullable: true })
  releaseDate?: Date | null;

  // Import branch: reference of the DO / release order sent to the customer.
  @Column({ name: 'release_order_reference', type: 'varchar', length: 100, nullable: true })
  releaseOrderReference?: string | null;

  // Import branch: when the goods were handed over to the customer (proof of delivery).
  @Column({ name: 'delivered_at', type: 'timestamptz', nullable: true })
  deliveredAt?: Date | null;

  @Column({ name: 'gate_cleared_at', type: 'timestamptz', nullable: true })
  gateClearedAt?: Date | null;

  @Column({ name: 'notes', type: 'text', nullable: true })
  notes?: string | null;
}
