import { Type } from 'class-transformer';
import {
  IsArray,
  IsDateString,
  IsIn,
  IsNotEmpty,
  IsNumber,
  IsOptional,
  IsPositive,
  IsString,
  IsUUID,
  MaxLength,
  Min,
  ValidateIf,
  ValidateNested,
} from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';

export class ChequeDataDto {
  @ApiProperty()
  @IsString()
  @IsNotEmpty()
  @MaxLength(50)
  numero_cheque: string;

  @ApiProperty()
  @IsString()
  @IsNotEmpty()
  @MaxLength(100)
  banco_emisor: string;

  @ApiProperty()
  @IsString()
  @IsNotEmpty()
  @MaxLength(200)
  titular: string;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  @MaxLength(50)
  cuenta_bancaria_origen?: string;

  @ApiProperty()
  @IsDateString()
  fecha_emision: string;

  @ApiPropertyOptional({ description: 'Si va a una fecha futura → diferido' })
  @IsOptional()
  @IsDateString()
  fecha_vencimiento?: string;
}

export class RecMedioPagoDto {
  @ApiProperty({ enum: ['EFECTIVO', 'CHEQUE', 'TRANSFERENCIA', 'TARJETA', 'SALDO_FAVOR', 'OTRO'] })
  @IsIn(['EFECTIVO', 'CHEQUE', 'TRANSFERENCIA', 'TARJETA', 'SALDO_FAVOR', 'OTRO'])
  medio: string;

  @ApiPropertyOptional({ type: ChequeDataDto, description: 'Datos del cheque (obligatorio si medio=CHEQUE)' })
  @ValidateIf((o) => o.medio === 'CHEQUE')
  @ValidateNested()
  @Type(() => ChequeDataDto)
  cheque?: ChequeDataDto;

  @ApiProperty()
  @IsNumber({ maxDecimalPlaces: 2 })
  @IsPositive()
  monto: number;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  moneda?: string;

  @ApiPropertyOptional()
  @IsOptional()
  @IsNumber({ maxDecimalPlaces: 4 })
  tipo_cambio?: number;

  @ApiPropertyOptional({ description: 'Cuenta de Tesorería que recibe el pago. Si no se informa, se resuelve por regla COBRO_CLIENTE.' })
  @IsOptional()
  @IsUUID()
  cuenta_tesoreria_id?: string;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  @MaxLength(100)
  referencia?: string;

  @ApiPropertyOptional({ description: 'ID de saldo a favor (solo si medio=SALDO_FAVOR)' })
  @ValidateIf((o) => o.medio === 'SALDO_FAVOR')
  @IsUUID()
  saldo_favor_id?: string;
}

export class RecRetencionDto {
  @ApiProperty({ description: 'ID de la línea de factura dentro del recibo a la que aplica' })
  @IsString()
  factura_ref: string; // Referencia temporal por factura_id para vincular al crear

  @ApiProperty({ enum: ['IVA', 'RENTA'] })
  @IsIn(['IVA', 'RENTA'])
  tipo: string;

  @ApiProperty()
  @IsNumber({ maxDecimalPlaces: 2 })
  @Min(0)
  monto: number;

  @ApiPropertyOptional()
  @IsOptional()
  @IsNumber({ maxDecimalPlaces: 2 })
  porcentaje?: number;

  @ApiProperty()
  @IsString()
  @IsNotEmpty()
  numero_comprobante: string;

  @ApiProperty()
  @IsString()
  @IsNotEmpty()
  timbrado: string;

  @ApiProperty()
  @IsDateString()
  fecha_comprobante: string;

  @ApiPropertyOptional()
  @IsOptional()
  @IsUUID()
  regimen_id?: string;

  @ApiPropertyOptional({ description: 'URL pública del comprobante (PDF/imagen) subido previamente al endpoint /comprobante-retencion' })
  @IsOptional()
  @IsString()
  @MaxLength(500)
  archivo_url?: string;
}

export class RecNcAplicadaDto {
  @ApiProperty()
  @IsUUID()
  nota_credito_id: string;

  @ApiProperty({ description: 'factura_id destino de la NC (puede diferir de la factura original si modo=FLEXIBLE)' })
  @IsUUID()
  factura_id_destino: string;

  @ApiProperty()
  @IsNumber({ maxDecimalPlaces: 2 })
  @IsPositive()
  monto: number;
}

export class RecGastoAplicadoDto {
  @ApiProperty({ description: 'ID del gasto (gasto_cab) con CxP pendiente' })
  @IsUUID()
  gasto_id: string;

  @ApiProperty({ description: 'Monto a descontar del cobro (≤ saldo CxP del gasto)' })
  @IsNumber({ maxDecimalPlaces: 2 })
  @IsPositive()
  monto_aplicado: number;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  @MaxLength(500)
  observacion?: string;
}

export class RecFacturaCompraAplicadaDto {
  @ApiProperty({ description: 'ID de la compra (factura por acuerdo comercial que el cliente le emitió a la empresa)' })
  @IsUUID()
  compra_cab_id: string;

  @ApiProperty({ description: 'Monto a compensar contra la deuda del cliente' })
  @IsNumber({ maxDecimalPlaces: 2 })
  @IsPositive()
  monto: number;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  @MaxLength(500)
  observacion?: string;
}

export class RecFacturaItemDto {
  @ApiProperty()
  @IsUUID()
  factura_id: string;

  @ApiPropertyOptional({ description: 'ID de cuota específica (para ventas a crédito)' })
  @IsOptional()
  @IsUUID()
  cuota_id?: string;

  @ApiProperty({ description: 'Monto a imputar de esta factura/cuota' })
  @IsNumber({ maxDecimalPlaces: 2 })
  @Min(0)
  monto_pagado: number;
}

export class CreateReciboDto {
  @ApiProperty()
  @IsUUID()
  cliente_id: string;

  @ApiPropertyOptional({ description: 'Sucursal. Si no se envía, se usa la primera activa de la empresa.' })
  @IsOptional()
  @IsUUID()
  sucursal_id?: string;

  @ApiPropertyOptional()
  @IsOptional()
  @IsUUID()
  cobrador_id?: string;

  @ApiPropertyOptional()
  @IsOptional()
  @IsDateString()
  fecha_registro?: string;

  @ApiPropertyOptional({ description: 'Fecha de emisión del recibo. Si no se envía, se usa la fecha actual.' })
  @IsOptional()
  @IsDateString()
  fecha_emision?: string;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  moneda?: string;

  @ApiPropertyOptional()
  @IsOptional()
  @IsNumber({ maxDecimalPlaces: 4 })
  tipo_cambio?: number;

  @ApiProperty({ type: [RecFacturaItemDto] })
  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => RecFacturaItemDto)
  facturas: RecFacturaItemDto[];

  @ApiPropertyOptional({ type: [RecNcAplicadaDto] })
  @IsOptional()
  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => RecNcAplicadaDto)
  nc_aplicadas?: RecNcAplicadaDto[];

  @ApiPropertyOptional({ type: [RecRetencionDto] })
  @IsOptional()
  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => RecRetencionDto)
  retenciones?: RecRetencionDto[];

  @ApiPropertyOptional({ type: [RecFacturaCompraAplicadaDto], description: 'Facturas de compra (acuerdos comerciales) que el cliente emitió a la empresa y se compensan contra su deuda' })
  @IsOptional()
  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => RecFacturaCompraAplicadaDto)
  facturas_compra_aplicadas?: RecFacturaCompraAplicadaDto[];

  @ApiPropertyOptional({ type: [RecGastoAplicadoDto], description: 'Gastos documentados (comisión bancaria, flete, etc.) descontados del cobro. Solo gastos con CxP pendiente. Requiere permiso COB_REC_GASTOS y flag empresa habilitado.' })
  @IsOptional()
  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => RecGastoAplicadoDto)
  gastos?: RecGastoAplicadoDto[];

  @ApiProperty({ type: [RecMedioPagoDto] })
  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => RecMedioPagoDto)
  medios_pago: RecMedioPagoDto[];

  @ApiPropertyOptional({ enum: ['EXACTO', 'SALDO_FAVOR', 'PAGO_PARCIAL', 'AJUSTE_MANUAL'] })
  @IsOptional()
  @IsIn(['EXACTO', 'SALDO_FAVOR', 'PAGO_PARCIAL', 'AJUSTE_MANUAL'])
  diferencia_tipo?: string;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  @MaxLength(500)
  diferencia_motivo?: string;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  observacion?: string;
}

export class AnularReciboDto {
  @ApiProperty()
  @IsString()
  @IsNotEmpty()
  @MaxLength(500)
  motivo: string;
}

export class PreviewReciboDto extends CreateReciboDto {}
