import { Entity, Column, Index, Check, ManyToOne, OneToMany, JoinColumn } from 'typeorm';
import { BaseEntity } from '../database/base.entity';
import { BookingEntity } from './booking.entity';
import { BookingTransportItemSupplierEntity } from './booking-transport-item-supplier.entity';
import { TransportServiceEntity } from './transport-service.entity';
import { VehicleTypeEntity } from './vehicle-type.entity';
import { SupplierEntity } from './supplier.entity';
import { DriverEntity } from './driver.entity';
import { VehicleEntity } from './vehicle.entity';
import { BookingActivityItemEntity } from './booking-activity-item.entity';

@Entity('booking_transport_items')
// (tenant_id, booking_id) — accounting collectLines + MD getMovementChart
// scan all items for a booking; without this the plan falls back to a
// full scan on the item table. Partial index keeps it skinny.
@Index('ix_booking_transport_items_tenant_booking', ['tenant_id', 'booking_id'], {
  where: '"is_deleted" = false',
})
// (tenant_id, op_status) — MD Chart Done/Pending stats + dispatch views.
@Index('ix_bti_tenant_op_status', ['tenant_id', 'op_status'], {
  where: '"is_deleted" = false',
})
// Reject typos on the OpStatus varchar column at write time.
@Check(
  'chk_booking_transport_items_op_status',
  `"op_status" IN ('UNPLANNED','PLANNED','HOLD','PICKED_UP','NO_SHOW','CANCELLED')`,
)
export class BookingTransportItemEntity extends BaseEntity {
  @ManyToOne(() => BookingEntity, (b) => b.transport_items, { onDelete: 'CASCADE' })
  @JoinColumn({ name: 'booking_id' })
  booking: BookingEntity;

  @Column({ type: 'uuid' })
  booking_id: string;

  @ManyToOne(() => TransportServiceEntity)
  @JoinColumn({ name: 'transport_service_id' })
  transport_service: TransportServiceEntity;

  @Column({ type: 'uuid' })
  transport_service_id: string;

  /**
   * Vehicle type — FK into vehicle_types master. Nullable for SIC
   * lines. Pre-refactor this was a denormalised text column; FK
   * makes the selling-below-cost detector's join trivial (id = id)
   * and removes the vt-name index dependency.
   */
  @ManyToOne(() => VehicleTypeEntity, { nullable: true })
  @JoinColumn({ name: 'vehicle_type_id' })
  vehicle_type: VehicleTypeEntity | null;

  @Column({ type: 'uuid', nullable: true })
  vehicle_type_id: string | null;

  @Column({ type: 'int', default: 1 })
  quantity: number;

  @Column({ type: 'decimal', precision: 12, scale: 2, default: 0 })
  unit_price: number;

  @Column({ type: 'decimal', precision: 12, scale: 2, default: 0 })
  subtotal: number;

  @Column({ type: 'date', nullable: true })
  date: string | null;

  @Column({ type: 'int', default: 0 })
  sort_order: number;

  /** General remarks — kept for backward compat and the inline
   *  Remarks column on the Planner grid. Optional free-form text. */
  @Column({ type: 'text', nullable: true })
  notes: string | null;

  /** Pickup-specific remarks — surfaced in the Planner sub-row below
   *  the pickup address cell. Examples: "call guest 15 min before",
   *  "use staff entrance", "wheelchair access". */
  @Column({ type: 'text', nullable: true })
  pickup_notes: string | null;

  /** Drop-off-specific remarks — surfaced in the Planner sub-row
   *  below the drop-off address cell. Examples: "drop at lobby", "key
   *  with doorman". */
  @Column({ type: 'text', nullable: true })
  dropoff_notes: string | null;

  @Column({ type: 'boolean', default: false })
  is_shared: boolean;

  /**
   * Amended-after-booking flag.
   *
   * `true` when the row was added via Movement Chart's
   * "+ Add amended transport" action AFTER the booking was originally
   * confirmed. Used by:
   *   - Booking Detail / customer PDF — "Amended Services" section
   *     surfaces these rows separately from the original itinerary
   *   - Invoice — adds an Amended line under the main total
   *   - PaymentSettlementService — every amended row auto-creates a
   *     COLLECTION for the customer's outstanding balance
   *
   * Stays `false` for everything created at booking time or via the
   * regular itinerary editor (those flow through Change Requests).
   */
  @Column({ type: 'boolean', default: false })
  is_amended: boolean;

  /**
   * Per-trip ACTUAL supplier cost, recorded inline by the Ops Manager
   * on Movement Chart for finance reconciliation. Nullable — null
   * means "no override; use the master rate from supplier_cost_prices".
   *
   * Only Operations Manager + Admin can write this; it is internal
   * margin only and never affects the customer-facing total / invoice
   * / outstanding balance.
   */
  @Column({ type: 'decimal', precision: 12, scale: 2, nullable: true })
  actual_supplier_cost: number | null;

  // Per-row pax override. Null ⇒ inherit booking pax (default). Set
  // when one or more passengers on the booking are not travelling this
  // leg (partial cancellation, late arrival, separate drop, etc.). Ops
  // edits these inline from Movement Chart → Planner.
  @Column({ type: 'int', nullable: true })
  adults_override: number | null;

  @Column({ type: 'int', nullable: true })
  children_override: number | null;

  // --- Driver Assignment (Phase B — nullable for now) ---
  @ManyToOne(() => SupplierEntity, { nullable: true })
  @JoinColumn({ name: 'supplier_id' })
  supplier: SupplierEntity | null;

  @Column({ type: 'uuid', nullable: true })
  supplier_id: string | null;

  @ManyToOne(() => DriverEntity, { nullable: true })
  @JoinColumn({ name: 'driver_id' })
  driver: DriverEntity | null;

  @Column({ type: 'uuid', nullable: true })
  driver_id: string | null;

  /**
   * Free-text fallbacks for when the actual supplier / driver / vehicle
   * isn't a registered master — real-case: private taxi booked ad-hoc,
   * supplier dispatches a driver not yet in our system, etc.
   * If the corresponding *_id is set, the text field is ignored at read time.
   */
  @Column({ type: 'varchar', length: 255, nullable: true })
  supplier_name_text: string | null;

  // --- Secondary supplier (Supplier 2 in ops Excel) ---
  // Optional second supplier on the same trip — e.g. one for transport,
  // one for tickets/entry. Matches existing ops workflow.
  @Column({ type: 'uuid', nullable: true })
  supplier_2_id: string | null;

  @Column({ type: 'varchar', length: 255, nullable: true })
  supplier_2_name_text: string | null;

  @Column({ type: 'varchar', length: 255, nullable: true })
  driver_name_text: string | null;

  @Column({ type: 'varchar', length: 20, nullable: true })
  driver_phone_text: string | null;

  @Column({ type: 'varchar', length: 50, nullable: true })
  vehicle_no_text: string | null;

  @ManyToOne(() => VehicleEntity, { nullable: true })
  @JoinColumn({ name: 'vehicle_id' })
  vehicle: VehicleEntity | null;

  @Column({ type: 'uuid', nullable: true })
  vehicle_id: string | null;

  @Column({ type: 'varchar', length: 10, nullable: true })
  pickup_time: string | null;

  /**
   * Drop-off time at the destination. Stored as 24-hour HH:MM like
   * pickup_time. Optional — null when ops only captures pickup. Added
   * to support the booking/quote dispatcher flow where the driver
   * needs both ends of the trip.
   */
  @Column({ type: 'varchar', length: 10, nullable: true })
  dropoff_time: string | null;

  /**
   * Return-leg pickup time for two-way transports ("RTN" in ops
   * speak). Only meaningful when the underlying
   * transport_service.category === 'TWO_WAY'. Stored as HH:MM like
   * pickup_time. Null for one-way trips.
   */
  @Column({ type: 'varchar', length: 10, nullable: true })
  return_pickup_time: string | null;

  @Column({ type: 'varchar', length: 500, nullable: true })
  pickup_address: string | null;

  @Column({ type: 'varchar', length: 500, nullable: true })
  dropoff_address: string | null;

  @Column({ type: 'varchar', length: 20, nullable: true })
  assignment_status: string | null;

  /**
   * Operational lifecycle of this transport leg — see OpStatus enum.
   * Separate from `assignment_status` (driver wiring) and the parent
   * booking's status. Defaults to PLANNED on creation; terminal
   * states are DROPPED / NO_SHOW / CANCELLED.
   */
  @Column({ type: 'varchar', length: 20, default: 'PLANNED' })
  op_status: string;

  /** Timestamp of the last op_status transition — feeds "when did this
   *  get picked up?" questions and supports overdue detection. */
  @Column({ type: 'timestamp', nullable: true })
  op_status_at: Date | null;

  /**
   * Soft link to a same-day activity ("dedicated transport for this
   * activity"). When set, the FE renders this transport visually
   * grouped with the activity and co-moves them on drag / Move-to-day.
   * SET NULL on delete keeps the transport row alive when the parent
   * activity is removed — it just becomes a regular standalone leg.
   */
  @ManyToOne(() => BookingActivityItemEntity, { nullable: true, onDelete: 'SET NULL' })
  @JoinColumn({ name: 'linked_activity_id' })
  linked_activity: BookingActivityItemEntity | null;

  @Column({ type: 'uuid', nullable: true })
  linked_activity_id: string | null;

  // ── Tax snapshot (added 2026-05-07 — see migration 1777700000000) ────
  // Re-snapshotted at booking-create from quote (Q1=C); immutable
  // on edit unless refresh_tax: true (Q2=B).
  @Column({ type: 'jsonb', nullable: true })
  tax_breakdown: Record<string, Record<string, number>> | null;

  @Column({ type: 'decimal', precision: 12, scale: 2, nullable: true })
  tax_amount: number | null;

  /** Per-line customer-side slab snapshot (added in migration
   *  1781100000000). See query-quote-hotel.entity.ts for the policy. */
  @Column({ type: 'jsonb', nullable: true })
  tax_snapshot: Record<string, unknown> | null;

  /** Supplier-side slab snapshot tied to actual_supplier_cost.
   *  Movement Dashboard writes this on cost-cell save. */
  @Column({ type: 'jsonb', nullable: true })
  cost_tax_snapshot: Record<string, unknown> | null;

  /** Multi-supplier junction rows — the scalable replacement for
   *  `supplier_id` / `supplier_2_id`. Loaded via the relation in
   *  driver-assignment.service.getMovementChart so the FE Planner
   *  gets a `suppliers: [...]` array on each op. See migration
   *  1784000 for the phased cutover plan. */
  @OneToMany(
    () => BookingTransportItemSupplierEntity,
    (s) => s.transport_item,
  )
  suppliers: BookingTransportItemSupplierEntity[];
}
