import { Transform, Type } from 'class-transformer';
import {
  ArrayMinSize,
  IsArray,
  IsEnum,
  IsIn,
  IsNumber,
  IsOptional,
  IsString,
  IsUUID,
  MaxLength,
  Min,
  ValidateNested,
} from 'class-validator';
import { SettlementPaymentMode } from '../../../entities/settlement-payment.entity';
import { SettlementPaymentType } from '../../../common/constants/settlement-payment-type';
import {
  CollectionExcessTreatment,
  CollectionPartyType,
} from '../../../entities/payment-collection.entity';

/**
 * One row from the Payment Collection screen's outstanding grid —
 * how much of the received amount is being allocated against this
 * settlement.
 *
 * Service-layer guarantees:
 *   - amount ≤ settlement.outstanding (+ 0.005 fudge for decimal rounding)
 *   - settlement belongs to a booking whose guest_mobile matches the
 *     payload's customer_mobile
 *   - settlement.currency_id matches the payload's currency_id (same-
 *     currency policy)
 */
export class PaymentAllocationDto {
  @IsUUID()
  settlement_id: string;

  @IsNumber()
  @Min(0.01)
  amount: number;
}

/**
 * Body for `POST /payment-settlements/collect`. One submission of the
 * Payment Collection screen = one collection event + N child settlement
 * payments under a shared receipt_no.
 */
export class CollectPaymentDto {
  /** Defaults to CUSTOMER for backwards compatibility with the original
   *  customer-only flow. SOURCE = agent paying via trip_source. */
  @IsEnum(CollectionPartyType)
  @IsOptional()
  party_type?: CollectionPartyType;

  /** Required when party_type = 'CUSTOMER' (the default). Mutually
   *  exclusive with `source_id` per the DB CHECK. */
  @IsString()
  @IsOptional()
  @MaxLength(20)
  customer_mobile?: string;

  /** Required when party_type = 'SOURCE'. */
  @IsUUID()
  @IsOptional()
  source_id?: string;

  /** Snapshot of customer name (CUSTOMER) or agent name (SOURCE). */
  @IsString()
  @IsOptional()
  @MaxLength(200)
  customer_name?: string;

  @IsString()
  @MaxLength(20)
  payment_date: string;

  /** Defaults to BILL_TO_BILL. ON_ACCOUNT rejects allocations and
   *  parks the whole amount on the party's credit ledger. */
  @IsEnum(SettlementPaymentType)
  @IsOptional()
  payment_type?: SettlementPaymentType;

  @IsEnum(SettlementPaymentMode)
  mode: SettlementPaymentMode;

  @IsString()
  @IsOptional()
  @MaxLength(100)
  ref_no?: string;

  /** Optional on every payment mode. When both sides are set they must differ. */
  @Transform(({ value }) => (value === '' || value === null ? undefined : value))
  @IsOptional()
  @IsUUID()
  debit_bank_account_id?: string;

  /** Optional on every payment mode. When both sides are set they must differ. */
  @Transform(({ value }) => (value === '' || value === null ? undefined : value))
  @IsOptional()
  @IsUUID()
  credit_bank_account_id?: string;

  @IsUUID()
  currency_id: string;

  @IsNumber()
  @Min(0.01)
  received_amount: number;

  /** Currency the client paid in. Defaults to `currency_id` (bill) when omitted. */
  @IsUUID()
  @IsOptional()
  receive_currency_id?: string;

  /** Rate converting receive-currency amount into bill-currency `received_amount`. */
  @IsNumber()
  @IsOptional()
  @Min(0)
  exchange_rate?: number;

  /** The rate as entered in the natural "1 Bill = X Receive" direction
   *  (e.g. 1 AED = 26.37 INR). Stored verbatim for receipt/history. */
  @IsNumber()
  @IsOptional()
  @Min(0)
  exchange_rate_quote?: number;

  /** Amount received in `receive_currency_id`, before conversion. */
  @IsNumber()
  @IsOptional()
  @Min(0.01)
  received_amount_receive_currency?: number;

  /** Optional rate of exchange. Printed on the collection receipt. */
  @IsNumber()
  @IsOptional()
  @Min(0)
  roe?: number;

  /** Optional drawdown from the party's credit_ledger balance. Must be
   *  ≤ that balance (enforced server-side). Counted alongside
   *  `received_amount` when capping allocations. */
  @IsNumber()
  @IsOptional()
  @Min(0)
  credit_applied?: number;

  /** Empty array when the customer is paying purely as an advance and
   *  not allocating against any settlement. Service rejects when both
   *  `allocations` is empty AND `excess_treatment` is null. */
  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => PaymentAllocationDto)
  @ArrayMinSize(0)
  allocations: PaymentAllocationDto[];

  /** Required when received_amount > sum(allocations). */
  @IsEnum(CollectionExcessTreatment)
  @IsOptional()
  excess_treatment?: CollectionExcessTreatment;

  @IsString()
  @IsOptional()
  notes?: string;

  @IsOptional()
  @IsIn(['OWN', 'SUPPLIER'])
  collected_by_type?: 'OWN' | 'SUPPLIER';

  @IsString()
  @IsOptional()
  @MaxLength(255)
  collector_name?: string;

  @IsUUID()
  @IsOptional()
  collector_supplier_id?: string;

  @IsUUID()
  @IsOptional()
  collector_driver_id?: string;
}
