import {
  ArrayMaxSize,
  ArrayMinSize,
  IsArray,
  IsBoolean,
  IsEnum,
  IsNumber,
  IsOptional,
  IsString,
  MinLength,
  ValidateNested,
} from "class-validator";
import { Type } from "class-transformer";
import { Currency, TransactionDomain } from "@jorgecuadros/database";

/**
 * A new ledger movement. `amount` is signed: negative = cargo (charge),
 * positive = abono (credit) — the module's sign convention. Booked movements
 * are never edited; a mistake is corrected by voiding and re-capturing.
 */
export class CreateMovementDto {
  @IsString() @MinLength(1) customerId!: string;
  @IsEnum(TransactionDomain) domain!: TransactionDomain;
  @IsNumber() amount!: number;
  @IsString() @MinLength(1) transactionDate!: string;

  @IsOptional() @IsEnum(Currency) currency?: Currency;
  @IsOptional() @IsString() typeId?: string;
  @IsOptional() @IsString() period?: string;
  @IsOptional() @IsString() reference?: string;
  @IsOptional() @IsString() checkNumber?: string;
  @IsOptional() @IsString() message?: string;
  /**
   * Legacy "NOPAGO": the bill was captured but not actually paid (no funds).
   * The row posts normally and stays visible, but is kept out of every balance
   * aggregate until resolved — see BillingService's NOT_OUTSTANDING.
   */
  @IsOptional() @IsBoolean() outstanding?: boolean;
}

/**
 * Resolving an outstanding row: the check finally got cut, so the movement
 * takes the resolution date and check number and starts counting toward the
 * balance. Legacy behavior: "se actualiza registro con fecha del día y el
 * cheque a pagar y quitas outstanding".
 */
export class ResolveOutstandingDto {
  @IsString() @MinLength(1) checkNumber!: string;
  @IsString() @MinLength(1) resolvedDate!: string;
}

/** One customer's line within a batch; check-level fields live on the parent. */
export class BatchLineDto {
  @IsString() @MinLength(1) customerId!: string;
  @IsNumber() amount!: number;

  @IsOptional() @IsString() reference?: string;
  @IsOptional() @IsString() period?: string;
  @IsOptional() @IsString() message?: string;
  @IsOptional() @IsBoolean() outstanding?: boolean;
}

/**
 * Batch capture by check — the legacy "Editor" flow: key many customers'
 * receipts against one check, then reconcile the captured total against the
 * physical check. Deliberately NOT a persisted batch entity: `checkNumber` is
 * already a column, and grouping by it answers every legacy by-check query.
 */
export class BatchCreateDto {
  @IsEnum(TransactionDomain) domain!: TransactionDomain;
  @IsString() @MinLength(1) transactionDate!: string;
  @IsString() @MinLength(1) checkNumber!: string;

  @IsOptional() @IsEnum(Currency) currency?: Currency;
  @IsOptional() @IsString() typeId?: string;

  // Capped so one request can't open a transaction over an unbounded row set;
  // a physical check batch is tens of lines, not thousands.
  @IsArray()
  @ArrayMinSize(1)
  @ArrayMaxSize(500)
  @ValidateNested({ each: true })
  @Type(() => BatchLineDto)
  lines!: BatchLineDto[];
}
