import { ApiProperty, ApiPropertyOptional, PartialType } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import {
  IsBoolean,
  IsDateString,
  IsIn,
  IsNotEmpty,
  IsNumber,
  IsOptional,
  IsString,
  IsUUID,
  Matches,
  MaxLength,
  Min,
} from 'class-validator';

export const IMP_PERFILES = ['AUTOS', 'CONSUMO_MASIVO'] as const;
export const IMP_ESTADOS_EMBARQUE = [
  'BORRADOR',
  'EN_TRANSITO',
  'ARRIBADO',
  'EN_DESPACHO',
  'DESPACHADO',
  'LIBERADO',
  'CERRADO',
  'SELLADO',
] as const;
export const IMP_CRITERIOS_PRORRATEO = ['FOB', 'PESO', 'CBM', 'CANTIDAD', 'IGUAL'] as const;

export class CreateEmbarqueDto {
  @ApiPropertyOptional({
    enum: IMP_PERFILES,
    description:
      'Si se omite, el backend usa el perfil_activo configurado por administración. ' +
      'Si se envía y no coincide, se rechaza la operación.',
  })
  @IsOptional()
  @IsIn(IMP_PERFILES)
  perfil?: string;

  @ApiProperty({ description: 'Proveedor extranjero o despachante principal del embarque' })
  @IsUUID()
  @IsNotEmpty()
  proveedor_id: string;

  @ApiPropertyOptional({ description: 'Moneda de referencia del embarque' })
  @IsOptional()
  @IsUUID()
  moneda_id?: string;

  @ApiPropertyOptional({ example: 'KR' })
  @IsOptional()
  @IsString()
  @MaxLength(3)
  origen_pais?: string;

  @ApiPropertyOptional({ example: 'Busan' })
  @IsOptional()
  @IsString()
  @MaxLength(120)
  origen_puerto?: string;

  @ApiPropertyOptional({ example: 'Villeta' })
  @IsOptional()
  @IsString()
  @MaxLength(120)
  destino_puerto?: string;

  @ApiPropertyOptional({ example: 'RoRo' })
  @IsOptional()
  @IsString()
  @MaxLength(30)
  modalidad?: string;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  @MaxLength(80)
  booking?: string;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  @MaxLength(80)
  bill_of_lading?: string;

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

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

  @ApiPropertyOptional({ description: 'Fecha real de arribo registrada al recibir el embarque' })
  @IsOptional()
  @IsDateString()
  fecha_arribo_real?: string;

  @ApiPropertyOptional({ description: 'Cotización congelada al cerrar despacho (Gs/USD)' })
  @IsOptional()
  @Type(() => Number)
  @IsNumber({ maxDecimalPlaces: 2 })
  @Min(0)
  cotizacion_cierre?: number;

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

export class UpdateEmbarqueDto extends PartialType(CreateEmbarqueDto) {}

export class UpdateEstadoEmbarqueDto {
  @ApiProperty({ enum: IMP_ESTADOS_EMBARQUE })
  @IsIn(IMP_ESTADOS_EMBARQUE)
  estado: string;
}

export class CreateItemEmbarqueDto {
  @ApiPropertyOptional()
  @IsOptional()
  @IsUUID()
  producto_id?: string;

  @ApiPropertyOptional({ description: 'SKU interno mapeado para perfil CONSUMO_MASIVO' })
  @IsOptional()
  @IsUUID()
  sku_interno_id?: string;

  // ── Campos AUTOS ────────────────────────────────────────────────
  @ApiPropertyOptional({ description: 'Chasis/VIN para perfil AUTOS' })
  @IsOptional()
  @IsString()
  @MaxLength(40)
  chasis?: string;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  @MaxLength(80)
  motor?: string;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  @MaxLength(120)
  marca?: string;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  @MaxLength(120)
  modelo?: string;

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

  @ApiPropertyOptional()
  @IsOptional()
  @Type(() => Number)
  @IsNumber()
  @Min(1900)
  anio?: number;

  @ApiPropertyOptional()
  @IsOptional()
  @Type(() => Number)
  @IsNumber()
  @Min(0)
  cilindrada_cc?: number;

  @ApiPropertyOptional()
  @IsOptional()
  @Type(() => Number)
  @IsNumber()
  @Min(0)
  kilometraje?: number;

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

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

  @ApiPropertyOptional({ description: 'Precio FOB en USD declarado en subasta' })
  @IsOptional()
  @Type(() => Number)
  @IsNumber({ maxDecimalPlaces: 2 })
  @Min(0)
  precio_fob_usd?: number;

  @ApiPropertyOptional({ description: 'Costo de inland en origen (USD)' })
  @IsOptional()
  @Type(() => Number)
  @IsNumber({ maxDecimalPlaces: 2 })
  @Min(0)
  inland_origen_usd?: number;

  @ApiPropertyOptional()
  @IsOptional()
  @Type(() => Number)
  @IsNumber()
  @Min(0)
  cantidad_duenios?: number;

  // ── Campos CONSUMO_MASIVO ───────────────────────────────────────
  @ApiPropertyOptional({ description: 'Código del proveedor para perfil CONSUMO_MASIVO' })
  @IsOptional()
  @IsString()
  @MaxLength(80)
  sku_proveedor?: string;

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

  @ApiPropertyOptional({ default: 1 })
  @IsOptional()
  @Type(() => Number)
  @IsNumber({ maxDecimalPlaces: 4 })
  @Min(0.0001)
  cantidad?: number;

  @ApiPropertyOptional({ default: 'UN' })
  @IsOptional()
  @IsString()
  @MaxLength(20)
  unidad?: string;

  @ApiPropertyOptional()
  @IsOptional()
  @Type(() => Number)
  @IsNumber({ maxDecimalPlaces: 4 })
  @Min(0)
  peso_unit_kg?: number;

  @ApiPropertyOptional()
  @IsOptional()
  @Type(() => Number)
  @IsNumber({ maxDecimalPlaces: 4 })
  @Min(0)
  cbm_unit?: number;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  @MaxLength(20)
  ncm?: string;

  @ApiPropertyOptional()
  @IsOptional()
  @Type(() => Number)
  @IsNumber({ maxDecimalPlaces: 2 })
  @Min(0)
  arancel_pct?: number;

  @ApiPropertyOptional()
  @IsOptional()
  @Type(() => Number)
  @IsNumber({ maxDecimalPlaces: 2 })
  @Min(0)
  isc_pct?: number;

  @ApiProperty({ description: 'FOB unitario en USD' })
  @Type(() => Number)
  @IsNumber({ maxDecimalPlaces: 4 })
  @Min(0)
  fob_unit_usd: number;

  @ApiPropertyOptional({ description: 'Orden de aparición en la lista del embarque' })
  @IsOptional()
  @Type(() => Number)
  @IsNumber()
  @Min(0)
  orden?: number;

  @ApiPropertyOptional({ description: 'Marca el ítem como mapeado a un SKU interno' })
  @IsOptional()
  @IsBoolean()
  mapeo_resuelto?: boolean;
}

export class UpdateItemEmbarqueDto extends PartialType(CreateItemEmbarqueDto) {}

export class CreateComponenteCostoDto {
  @ApiProperty()
  @IsUUID()
  concepto_id: string;

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

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

  @ApiPropertyOptional({ description: 'Proveedor que emite la factura del costo' })
  @IsOptional()
  @IsUUID()
  proveedor_id?: string;

  @ApiPropertyOptional({ description: 'Cuenta a pagar generada para este componente' })
  @IsOptional()
  @IsUUID()
  cuenta_pagar_id?: string;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  @MaxLength(200)
  descripcion?: string;

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

  @ApiProperty()
  @Type(() => Number)
  @IsNumber({ maxDecimalPlaces: 4 })
  @Min(0)
  importe: number;

  @ApiPropertyOptional({ default: 1 })
  @IsOptional()
  @Type(() => Number)
  @IsNumber({ maxDecimalPlaces: 6 })
  @Min(0.000001)
  cotizacion?: number;

  @ApiPropertyOptional({ default: false })
  @IsOptional()
  @IsBoolean()
  es_directo?: boolean;

  @ApiPropertyOptional({ enum: IMP_CRITERIOS_PRORRATEO, default: 'FOB' })
  @IsOptional()
  @IsIn(IMP_CRITERIOS_PRORRATEO)
  criterio_prorrateo?: string;

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

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

  @ApiPropertyOptional({ description: 'Marca componente cargado luego del cierre del despacho' })
  @IsOptional()
  @IsBoolean()
  es_posterior_cierre?: boolean;

  @ApiPropertyOptional({ description: 'Gasto extra de gestión cargado por administración' })
  @IsOptional()
  @IsBoolean()
  es_gasto_extra?: boolean;

  @ApiPropertyOptional({ description: 'Motivo obligatorio si es_gasto_extra=true' })
  @IsOptional()
  @IsString()
  motivo_gasto_extra?: string;

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

export class UpdateComponenteCostoDto extends PartialType(CreateComponenteCostoDto) {}

export class UpdateImportacionesConfigDto {
  @ApiPropertyOptional({ description: 'Activa/desactiva operativamente el módulo para la empresa' })
  @IsOptional()
  @IsBoolean()
  modulo_operativo_activo?: boolean;

  @ApiPropertyOptional({ enum: IMP_PERFILES, description: 'Perfil activo del módulo para la empresa' })
  @IsOptional()
  @IsIn(IMP_PERFILES)
  perfil_activo?: string;
}

// ── Fase 2: Despacho aduanero ──────────────────────────────────────

export class CreateDespachoDto {
  @ApiProperty({ description: 'Número de despacho (autorizado por aduana)' })
  @IsString()
  @IsNotEmpty()
  @MaxLength(100)
  numero_despacho: string;

  @ApiProperty({ description: 'Fecha del despacho (formato YYYY-MM-DD)' })
  @IsDateString()
  fecha_despacho: string;

  @ApiPropertyOptional({ description: 'Proveedor que oficia de despachante' })
  @IsOptional()
  @IsUUID()
  despachante_id?: string;

  @ApiPropertyOptional({ description: 'Arancel total liquidado (Gs.)' })
  @IsOptional()
  @Type(() => Number)
  @IsNumber()
  @Min(0)
  arancel_gs?: number;

  @ApiPropertyOptional({ description: 'IVA importación liquidado (Gs.)' })
  @IsOptional()
  @Type(() => Number)
  @IsNumber()
  @Min(0)
  iva_importacion_gs?: number;

  @ApiPropertyOptional({ description: 'ISC liquidado (Gs.)' })
  @IsOptional()
  @Type(() => Number)
  @IsNumber()
  @Min(0)
  isc_gs?: number;

  @ApiPropertyOptional({ description: 'INC liquidado (Gs.)' })
  @IsOptional()
  @Type(() => Number)
  @IsNumber()
  @Min(0)
  inc_gs?: number;

  @ApiPropertyOptional({ description: 'Anticipo IRE liquidado (Gs.)' })
  @IsOptional()
  @Type(() => Number)
  @IsNumber()
  @Min(0)
  anticipo_ire_gs?: number;

  @ApiPropertyOptional({ description: 'Tasa ANA liquidada (Gs.)' })
  @IsOptional()
  @Type(() => Number)
  @IsNumber()
  @Min(0)
  tasa_ana_gs?: number;

  @ApiPropertyOptional({ description: 'Otros tributos del despacho (Gs.)' })
  @IsOptional()
  @Type(() => Number)
  @IsNumber()
  @Min(0)
  otros_gs?: number;

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

export class UpdateDespachoDto extends PartialType(CreateDespachoDto) {}

export class LiberarEmbarqueDto {
  @ApiProperty({ description: 'Depósito destino donde ingresa la mercadería liberada' })
  @IsUUID()
  @IsNotEmpty()
  deposito_destino_id: string;
}

// ── Fase 2: Tasas ISC vehículos ────────────────────────────────────

export class CreateTasaIscDto {
  @ApiProperty({ minimum: 1900 })
  @Type(() => Number)
  @IsNumber()
  @Min(1900)
  anio_desde: number;

  @ApiProperty({ minimum: 1900 })
  @Type(() => Number)
  @IsNumber()
  @Min(1900)
  anio_hasta: number;

  @ApiProperty({ description: 'Cilindrada mínima (cc)' })
  @Type(() => Number)
  @IsNumber()
  @Min(0)
  cc_desde: number;

  @ApiProperty({ description: 'Cilindrada máxima (cc)' })
  @Type(() => Number)
  @IsNumber()
  @Min(0)
  cc_hasta: number;

  @ApiProperty({ description: 'Tasa ISC en porcentaje (ej: 5.00)' })
  @Type(() => Number)
  @IsNumber()
  @Min(0)
  tasa_isc_pct: number;

  @ApiPropertyOptional()
  @IsOptional()
  @IsString()
  @MaxLength(200)
  descripcion?: string;

  @ApiProperty({ description: 'Vigencia desde (YYYY-MM-DD)' })
  @IsDateString()
  vigente_desde: string;

  @ApiPropertyOptional({ description: 'Vigencia hasta (YYYY-MM-DD), null = sin vencimiento' })
  @IsOptional()
  @IsDateString()
  vigente_hasta?: string;

  @ApiPropertyOptional({ default: true })
  @IsOptional()
  @IsBoolean()
  activo?: boolean;
}

export class UpdateTasaIscDto extends PartialType(CreateTasaIscDto) {}

// ── Conceptos de costo (catálogo por empresa) ──────────────────────

export const IMP_GRUPOS_COSTO = ['ORIGEN', 'TRANSITO', 'ADUANA', 'LOGISTICA', 'FINANCIERO'] as const;
export const IMP_IMPUTACIONES = ['DIRECTO', 'COMPARTIDO'] as const;

export class CreateConceptoCostoDto {
  @ApiProperty({ description: 'Código interno único por empresa (mayúsculas, números y guion bajo)', maxLength: 40 })
  @IsString()
  @MaxLength(40)
  @Matches(/^[A-Z0-9_]+$/, { message: 'El código solo puede tener mayúsculas, números y guion bajo (ej: FLETE_AEREO)' })
  codigo: string;

  @ApiProperty({ description: 'Nombre visible del concepto', maxLength: 150 })
  @IsString()
  @MaxLength(150)
  nombre: string;

  @ApiPropertyOptional({ enum: IMP_GRUPOS_COSTO })
  @IsOptional()
  @IsIn(IMP_GRUPOS_COSTO)
  grupo?: string;

  @ApiPropertyOptional({ enum: IMP_IMPUTACIONES, default: 'COMPARTIDO' })
  @IsOptional()
  @IsIn(IMP_IMPUTACIONES)
  imputacion_default?: string;

  @ApiPropertyOptional({ enum: IMP_CRITERIOS_PRORRATEO, description: 'Solo aplica si imputacion_default = COMPARTIDO' })
  @IsOptional()
  @IsIn(IMP_CRITERIOS_PRORRATEO)
  criterio_prorrateo_default?: string;

  @ApiPropertyOptional({ default: 'USD', enum: ['USD', 'GS'] })
  @IsOptional()
  @IsIn(['USD', 'GS'])
  moneda_default?: string;

  @ApiPropertyOptional({ enum: IMP_PERFILES, description: 'Vacío = aplica a ambos perfiles' })
  @IsOptional()
  @IsIn(IMP_PERFILES)
  aplica_perfil?: string;
}

export class UpdateConceptoCostoDto extends PartialType(CreateConceptoCostoDto) {}

// ── Fase 2: Generación de Cuentas por Pagar ────────────────────────

export class CuotaCxPDto {
  @ApiProperty({ minimum: 1 })
  @Type(() => Number)
  @IsNumber()
  @Min(1)
  numero_cuota: number;

  @ApiProperty({ description: 'Fecha de vencimiento (YYYY-MM-DD)' })
  @IsDateString()
  fecha_vencimiento: string;

  @ApiProperty({ description: 'Monto de la cuota en moneda original' })
  @Type(() => Number)
  @IsNumber()
  @Min(0)
  monto: number;

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

export class GenerarCxPDto {
  @ApiPropertyOptional({
    description:
      'Proveedor para la CxP. Si no se envía, usa el del componente de costo. Si tampoco está, falla.',
  })
  @IsOptional()
  @IsUUID()
  proveedor_id?: string;

  @ApiProperty({ description: 'Fecha de emisión (YYYY-MM-DD)' })
  @IsDateString()
  fecha_emision: string;

  @ApiPropertyOptional({ description: 'Número de factura del proveedor' })
  @IsOptional()
  @IsString()
  @MaxLength(100)
  numero_factura?: string;

  @ApiPropertyOptional({
    description:
      'Días de plazo si no se envían cuotas explícitas. 0 = contado (vence el día de emisión).',
    default: 0,
  })
  @IsOptional()
  @Type(() => Number)
  @IsNumber()
  @Min(0)
  plazo_credito_dias?: number;

  @ApiPropertyOptional({
    type: [CuotaCxPDto],
    description: 'Cuotas explícitas. Si se envían, ignoran plazo_credito_dias.',
  })
  @IsOptional()
  @Type(() => CuotaCxPDto)
  cuotas?: CuotaCxPDto[];
}
