import { Entity, Column, Index, ManyToOne, JoinColumn, OneToMany } from 'typeorm';
import { BaseEntity } from '../database/base.entity';
import { BookingEntity } from './booking.entity';
import { CurrencyEntity } from './currency.entity';
import { PaymentSettlementEntity } from './payment-settlement.entity';

/**
 * Customer-side rollup status — derived from the customer's collection
 * + refund children.
 *
 *   PENDING     nothing collected yet (or no children at all)
 *   PARTIAL     some collected, more outstanding
 *   PAID        every customer COLLECTION fully settled
 *   REFUND_DUE  at least one unsettled REFUND child — wins over PAID
 */
export enum CustomerSettlementStatus {
  PENDING = 'PENDING',
  PARTIAL = 'PARTIAL',
  PAID = 'PAID',
  REFUND_DUE = 'REFUND_DUE',
}

/**
 * Supplier-side rollup status — derived from the supplier PAYOUT
 * children (across all suppliers on the booking).
 *
 *   PENDING     no payouts started
 *   PARTIAL     some suppliers paid in full or in part, more pending
 *   PAID        every supplier child settled
 */
export enum SupplierSettlementStatus {
  PENDING = 'PENDING',
  PARTIAL = 'PARTIAL',
  PAID = 'PAID',
}

/**
 * Overall booking-level status — what the list page renders as the pill.
 *
 *   PENDING      nothing has moved on either side
 *   IN_PROGRESS  some movement on customer or supplier, not fully cleared
 *   COMPLETED    customer fully collected AND every supplier paid
 *   HOLD         admin paused; mandatory reason
 *   REFUND_DUE   customer side has an unsettled refund — finance must act
 *                (wins over IN_PROGRESS / COMPLETED so it's never hidden)
 */
export enum OverallSettlementStatus {
  PENDING = 'PENDING',
  IN_PROGRESS = 'IN_PROGRESS',
  COMPLETED = 'COMPLETED',
  HOLD = 'HOLD',
  REFUND_DUE = 'REFUND_DUE',
}

/**
 * Booking Settlement Master — ONE row per booking.
 *
 * Customer + supplier rollups live as columns inside this single row
 * (not as separate master rows) because finance always wants to see
 * both sides + margin when they look at a booking. Supplier breakdown
 * across multiple suppliers on the same booking lives at the children
 * level (`payment_settlements.supplier_id`) — drill from this master
 * into the children to see per-supplier amounts.
 *
 * Created lazily by `PaymentSettlementService.ensureMaster` on the
 * first child write for a booking. Every subsequent child mutation
 * calls `recomputeMaster` to keep the rollup totals + statuses in sync.
 *
 * HOLD action (admin only):
 *   - `placeOnHold(master_id, reason)` — pauses auto-delta paths
 *   - `releaseHold(master_id)` — clears HOLD, recomputes status
 */
@Entity('booking_settlement_masters')
@Index(['tenant_id', 'overall_status', 'last_activity_at'])
@Index(['tenant_id', 'booking_id'])
export class BookingSettlementMasterEntity extends BaseEntity {
  @ManyToOne(() => BookingEntity, { onDelete: 'CASCADE' })
  @JoinColumn({ name: 'booking_id' })
  booking: BookingEntity;

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

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

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

  // ── Customer-side rollup ─────────────────────────────────────────────
  /** Σ amount on active children WHERE party=CUSTOMER, type=COLLECTION. */
  @Column({ type: 'decimal', precision: 12, scale: 2, default: 0 })
  customer_expected: number;

  /** Σ amount_settled on those same children — money actually received. */
  @Column({ type: 'decimal', precision: 12, scale: 2, default: 0 })
  customer_received: number;

  /** Σ (amount − amount_settled) on active CUSTOMER REFUND children. */
  @Column({ type: 'decimal', precision: 12, scale: 2, default: 0 })
  customer_refund_due: number;

  @Column({
    type: 'varchar',
    length: 20,
    default: CustomerSettlementStatus.PENDING,
  })
  customer_status: CustomerSettlementStatus;

  // ── Supplier-side rollup (across ALL suppliers on this booking) ──────
  /** Σ amount on active children WHERE party=SUPPLIER. */
  @Column({ type: 'decimal', precision: 12, scale: 2, default: 0 })
  supplier_expected: number;

  /** Σ amount_settled on those — money actually paid out to suppliers. */
  @Column({ type: 'decimal', precision: 12, scale: 2, default: 0 })
  supplier_paid: number;

  @Column({
    type: 'varchar',
    length: 20,
    default: SupplierSettlementStatus.PENDING,
  })
  supplier_status: SupplierSettlementStatus;

  // ── Margin ───────────────────────────────────────────────────────────
  /**
   * Planned margin — `customer_expected − supplier_expected`. Based on
   * SETTLEMENTS RAISED, not booking line items. Misleading when
   * suppliers haven't been assigned yet — use `cost_margin` for the
   * honest number.
   */
  @Column({ type: 'decimal', precision: 12, scale: 2, default: 0 })
  planned_margin: number;

  /**
   * Realised margin — `customer_received − supplier_paid`. The cash-
   * basis margin so far. Can exceed planned_margin transiently while
   * customer money is in but suppliers haven't been paid yet.
   */
  @Column({ type: 'decimal', precision: 12, scale: 2, default: 0 })
  realised_margin: number;

  /**
   * Expected cost basis — Σ actual_supplier_cost across booking line
   * items (transport + activity). Snapshotted at booking-create from
   * the Floor. Reflects Mellow's planned cost basis from day one,
   * independent of whether suppliers have been assigned. Drives the
   * honest `cost_margin` below.
   */
  @Column({ type: 'decimal', precision: 12, scale: 2, default: 0 })
  cost_expected: number;

  /**
   * Honest margin — `customer_expected − cost_expected`. Doesn't lie
   * when suppliers haven't been picked yet. Use this on the booking
   * detail / list page; `planned_margin` is a settlements-eye view
   * useful only for "how much have I raised payables on?"
   */
  @Column({ type: 'decimal', precision: 12, scale: 2, default: 0 })
  cost_margin: number;

  // ── Overall + admin ──────────────────────────────────────────────────
  @Column({
    type: 'varchar',
    length: 20,
    default: OverallSettlementStatus.PENDING,
  })
  overall_status: OverallSettlementStatus;

  /** Mandatory when overall_status=HOLD (DB CHECK constraint enforces). */
  @Column({ type: 'text', nullable: true })
  hold_reason: string | null;

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

  @Column({ type: 'timestamptz', nullable: true })
  hold_set_at: Date | null;

  /**
   * Stamped on every child mutation (create / payment / void / delta).
   * Drives the list-page recency sort.
   */
  @Column({ type: 'timestamptz' })
  last_activity_at: Date;

  @OneToMany(() => PaymentSettlementEntity, (s) => s.master)
  children: PaymentSettlementEntity[];
}
