import { Entity, Column, Index, Check, ManyToOne, OneToMany, JoinColumn } from 'typeorm';
import { BaseEntity } from '../database/base.entity';
import { BookingEntity } from './booking.entity';
import { BookingTransportItemEntity } from './booking-transport-item.entity';
import { BookingActivityItemEntity } from './booking-activity-item.entity';
import { BookingSpecialItemEntity } from './booking-special-item.entity';
import { BookingTransportItemSupplierEntity } from './booking-transport-item-supplier.entity';
import { BookingActivityItemSupplierEntity } from './booking-activity-item-supplier.entity';
import { BookingSpecialItemSupplierEntity } from './booking-special-item-supplier.entity';
import { CurrencyEntity } from './currency.entity';
import { ServicePaymentCollectionConfirmationEntity } from './service-payment-collection-confirmation.entity';

/**
 * Movement Dashboard — "collect cash from guest at the service".
 *
 * ## Domain
 * At the ground level, a driver or supplier physically collects money
 * from the traveller when a service is delivered — cash on delivery
 * for a transfer, a walk-in ticket, a special add-on. This entity is
 * the REQUIREMENT (the plan): one row per supplier assignment that
 * needs a collection. Actual money-in events live on the CONFIRMATION
 * table (`service_payment_collection_confirmations`) so amendments,
 * multi-tranche collections, and cancellations are natively
 * event-sourced.
 *
 * ## Polymorphic FK — exactly one of (transport, activity, special)
 * Each row references ONE service item on the booking via a
 * type-specific FK. A CHECK constraint enforces that exactly one of
 * the three item FKs is non-null AND `service_type` matches.
 *
 * ## Supplier scope
 * Each row also references ONE junction supplier row via the matching
 * `*_item_supplier_id` FK so multi-supplier lines can carry
 * independent collection requirements per supplier/driver split.
 *
 * ## Status
 * Denormalised snapshot of the latest confirmation's outcome:
 *   PENDING   — required = true, no active confirmation yet
 *   COLLECTED — at least one confirmation exists and is not voided
 *   CANCELLED — ops cancelled the requirement (no confirmations recorded)
 *
 * ## Uniqueness
 * A given supplier junction row can carry AT MOST one live collection
 * requirement. Enforced by three per-type partial UNIQUE indexes on
 * the junction FK columns.
 *
 * ## Ledger link
 * When a confirmation is recorded, the service creates a matching
 * `settlement_payments` row against the booking's customer receivable
 * and stores its id on the confirmation.
 */
@Entity('service_payment_collections')
@Index('ix_spc_tenant_booking', ['tenant_id', 'booking_id'], {
  where: '"is_deleted" = false',
})
@Index('ix_spc_tenant_status', ['tenant_id', 'status'], {
  where: '"is_deleted" = false',
})
@Index('ux_spc_transport_item_supplier', ['transport_item_supplier_id'], {
  unique: true,
  where:
    '"is_deleted" = false AND "status" <> \'CANCELLED\' AND "transport_item_supplier_id" IS NOT NULL',
})
@Index('ux_spc_activity_item_supplier', ['activity_item_supplier_id'], {
  unique: true,
  where:
    '"is_deleted" = false AND "status" <> \'CANCELLED\' AND "activity_item_supplier_id" IS NOT NULL',
})
@Index('ux_spc_special_item_supplier', ['special_item_supplier_id'], {
  unique: true,
  where:
    '"is_deleted" = false AND "status" <> \'CANCELLED\' AND "special_item_supplier_id" IS NOT NULL',
})
@Check(
  'chk_spc_service_type',
  `"service_type" IN ('TRANSPORT','ACTIVITY','SPECIAL')`,
)
@Check('chk_spc_status', `"status" IN ('PENDING','COLLECTED','CANCELLED')`)
@Check('chk_spc_expected_non_neg', `"expected_amount" >= 0`)
@Check(
  'chk_spc_item_fk_matches_type',
  `(
    ("service_type" = 'TRANSPORT' AND "transport_item_id" IS NOT NULL AND "activity_item_id" IS NULL AND "special_item_id" IS NULL) OR
    ("service_type" = 'ACTIVITY'  AND "activity_item_id"  IS NOT NULL AND "transport_item_id" IS NULL AND "special_item_id" IS NULL) OR
    ("service_type" = 'SPECIAL'   AND "special_item_id"   IS NOT NULL AND "transport_item_id" IS NULL AND "activity_item_id" IS NULL)
  )`,
)
@Check(
  'chk_spc_junction_fk_matches_type',
  `(
    ("service_type" = 'TRANSPORT' AND "transport_item_supplier_id" IS NOT NULL AND "activity_item_supplier_id" IS NULL AND "special_item_supplier_id" IS NULL) OR
    ("service_type" = 'ACTIVITY'  AND "activity_item_supplier_id"  IS NOT NULL AND "transport_item_supplier_id" IS NULL AND "special_item_supplier_id" IS NULL) OR
    ("service_type" = 'SPECIAL'   AND "special_item_supplier_id"   IS NOT NULL AND "transport_item_supplier_id" IS NULL AND "activity_item_supplier_id" IS NULL)
  )`,
)
export class ServicePaymentCollectionEntity extends BaseEntity {
  @ManyToOne(() => BookingEntity, { onDelete: 'CASCADE' })
  @JoinColumn({ name: 'booking_id' })
  booking: BookingEntity;

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

  @Column({ type: 'varchar', length: 20 })
  service_type: 'TRANSPORT' | 'ACTIVITY' | 'SPECIAL';

  @ManyToOne(() => BookingTransportItemEntity, { nullable: true, onDelete: 'CASCADE' })
  @JoinColumn({ name: 'transport_item_id' })
  transport_item: BookingTransportItemEntity | null;

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

  @ManyToOne(() => BookingActivityItemEntity, { nullable: true, onDelete: 'CASCADE' })
  @JoinColumn({ name: 'activity_item_id' })
  activity_item: BookingActivityItemEntity | null;

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

  @ManyToOne(() => BookingSpecialItemEntity, { nullable: true, onDelete: 'CASCADE' })
  @JoinColumn({ name: 'special_item_id' })
  special_item: BookingSpecialItemEntity | null;

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

  @ManyToOne(() => BookingTransportItemSupplierEntity, { nullable: true })
  @JoinColumn({ name: 'transport_item_supplier_id' })
  transport_item_supplier: BookingTransportItemSupplierEntity | null;

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

  @ManyToOne(() => BookingActivityItemSupplierEntity, { nullable: true })
  @JoinColumn({ name: 'activity_item_supplier_id' })
  activity_item_supplier: BookingActivityItemSupplierEntity | null;

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

  @ManyToOne(() => BookingSpecialItemSupplierEntity, { nullable: true })
  @JoinColumn({ name: 'special_item_supplier_id' })
  special_item_supplier: BookingSpecialItemSupplierEntity | null;

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

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

  @ManyToOne(() => CurrencyEntity)
  @JoinColumn({ name: 'currency_id' })
  currency: CurrencyEntity;

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

  @Column({ type: 'varchar', length: 20, default: 'PENDING' })
  status: 'PENDING' | 'COLLECTED' | 'CANCELLED';

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

  @OneToMany(
    () => ServicePaymentCollectionConfirmationEntity,
    (c) => c.collection,
  )
  confirmations: ServicePaymentCollectionConfirmationEntity[];
}
