import { Entity, Column, Index, ManyToOne, JoinColumn } from 'typeorm';
import { BaseEntity } from '../database/base.entity';
import { PaymentSettlementEntity } from './payment-settlement.entity';
import { PaymentCollectionEntity } from './payment-collection.entity';

/**
 * Payment mode — enumerated to keep reports clean. Add new modes via
 * migration (not via the DTO `@IsString()`); we want a closed set so
 * the dashboard "settled by mode" pie chart stays sane.
 */
export enum SettlementPaymentMode {
  CASH = 'CASH',
  CARD = 'CARD',
  BANK_TRANSFER = 'BANK_TRANSFER',
  UPI = 'UPI',
  WALLET = 'WALLET',
  CHEQUE = 'CHEQUE',
}

/**
 * Settlement Payment — append-only ledger row, one per Settle action
 * (collection receipt or refund payout) recorded against a parent
 * settlement.
 *
 * Stakeholder confirmed multiple-partials behaviour: a deposit / mid-
 * trip / final-balance flow produces three rows here against one
 * parent settlement, and the parent's `amount_settled` is the running
 * sum maintained by `PaymentSettlementService.recordPayment`.
 *
 * Never edited or deleted post-create. If a payment was logged in
 * error, finance issues a reverse adjustment (separate refund
 * settlement) rather than mutating history — keeps the audit trail
 * intact.
 */
@Entity('settlement_payments')
@Index(['tenant_id', 'settlement_id'])
@Index(['tenant_id', 'paid_date'])
export class SettlementPaymentEntity extends BaseEntity {
  @ManyToOne(() => PaymentSettlementEntity, (s) => s.payments, { onDelete: 'CASCADE' })
  @JoinColumn({ name: 'settlement_id' })
  settlement: PaymentSettlementEntity;

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

  /**
   * Always positive. For REFUND-type parent settlements, the sign is
   * still positive here — the parent's `type` carries the direction.
   * Service-layer guard rejects values that would push the parent's
   * running total above its `amount`.
   */
  @Column({ type: 'decimal', precision: 12, scale: 2 })
  paid_amount: number;

  @Column({ type: 'date' })
  paid_date: string;

  @Column({ type: 'enum', enum: SettlementPaymentMode })
  mode: SettlementPaymentMode;

  /**
   * External reference — bank ref / UPI txn ID / cheque no. Nullable
   * for CASH where no reference exists. Recommended for everything
   * else but not enforced (some tenants run loose).
   */
  @Column({ type: 'varchar', length: 100, nullable: true })
  ref_no: string | null;

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

  // ── Phase F: receipt number ─────────────────────────────────────
  // Stamped at create-time by PaymentSettlementService.recordPayment
  // via the settlement_receipt_sequences atomic counter. Prefix is
  // `RCP` for COLLECTION-type parents, `RFD` for REFUND-type. The
  // year-bucket resets the sequence so receipt numbers stay short.
  @Column({ type: 'varchar', length: 20, nullable: true })
  receipt_no: string | null;

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

  // ── Phase F: admin void ─────────────────────────────────────────
  // A voided payment stays in the ledger for audit but is excluded
  // from the parent's amount_settled rollup. Reversal is admin-only;
  // the void_reason is mandatory at the controller layer.
  @Column({ type: 'boolean', default: false })
  is_void: boolean;

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

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

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

  /** Per-instalment tax-bifurcation snapshot (added in migration
   *  1781100000000). Captures the customer-side slab at the moment the
   *  instalment was recorded, so the parked accountant report can
   *  prorate VAT collected / Tourism collected / etc. across
   *  instalments without re-resolving the slab at report time. */
  @Column({ type: 'jsonb', nullable: true })
  tax_snapshot: Record<string, unknown> | null;

  /** Phase A — Payment Collection link. NULL for legacy per-settlement
   *  payments (the Settle Dialog flow); set for child rows created by
   *  the new collect-payment flow. ON DELETE SET NULL — voiding the
   *  collection doesn't cascade-destroy the children; the void path
   *  marks them is_void=true instead so audit history stays intact. */
  @ManyToOne(() => PaymentCollectionEntity, (c) => c.payments, { onDelete: 'SET NULL' })
  @JoinColumn({ name: 'collection_id' })
  collection: PaymentCollectionEntity | null;

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

  /** §12e Pay Supplier link. Same semantics as collection_id but on
   *  the OUT side: NULL for legacy per-settlement supplier payments,
   *  set for child rows created by the supplier-payout flow. */
  @Column({ type: 'uuid', nullable: true })
  payout_id: string | null;
}
