import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Transform, Type } from 'class-transformer';
import {
  IsArray,
  IsBoolean,
  IsDateString,
  IsIn,
  IsInt,
  IsNumber,
  IsOptional,
  IsString,
  IsUUID,
  Matches,
  Min,
  ValidateNested,
} from 'class-validator';

import { PAYMENT_CURRENCIES } from './create-contract.dto';

/** Per-shipment equipment return — "NA" stays contract-level only. */
const SHIPMENT_EQUIPMENT_RETURNS = ['WITH_RETURN', 'WITHOUT_RETURN'] as const;

/** One physical container under a booking line — entered at booking time. */
export class CreateContainerUnitDto {
  @ApiProperty({ description: 'ISO 6346 container number, e.g. ABCD1234567' })
  @IsString()
  @Transform(({ value }) => (typeof value === 'string' ? value.trim().toUpperCase() : value))
  @Matches(/^[A-Z]{4}\d{7}$/, {
    message: 'containerNumber must match ISO container format, e.g. ABCD1234567',
  })
  containerNumber!: string;

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

  @ApiProperty({ description: 'VGM in tons', minimum: 0 })
  @IsNumber()
  @Min(0)
  @Transform(({ value }) => Number(value))
  vgmTons!: number;

  @ApiPropertyOptional({ default: false })
  @IsOptional()
  @IsBoolean()
  @Transform(({ value }) => value === 'true' || value === true)
  isHazardous?: boolean;

  @ApiPropertyOptional({ default: false })
  @IsOptional()
  @IsBoolean()
  @Transform(({ value }) => value === 'true' || value === true)
  isReefer?: boolean;

  @ApiPropertyOptional({
    default: false,
    description: 'This container ships back empty (equipment return).',
  })
  @IsOptional()
  @IsBoolean()
  @Transform(({ value }) => value === 'true' || value === true)
  isReturn?: boolean;
}

export class CreateBookingContainerLineDto {
  @ApiProperty({ description: '"20ft" | "40ft" — must be in the contract scope' })
  @IsString()
  containerSize!: string;

  @ApiProperty({ minimum: 1 })
  @IsInt()
  @Min(1)
  @Transform(({ value }) => Number(value))
  quantity!: number;

  @ApiPropertyOptional({ minimum: 0 })
  @IsOptional()
  @IsInt()
  @Min(0)
  @Transform(({ value }) => Number(value))
  hazardousQuantity?: number;

  @ApiPropertyOptional({ minimum: 0 })
  @IsOptional()
  @IsInt()
  @Min(0)
  @Transform(({ value }) => Number(value))
  reeferQuantity?: number;

  @ApiPropertyOptional({
    minimum: 0,
    description:
      'How many units of this line ship with empty-container return (≤ quantity). ' +
      'Only allowed when the contract was created WITH_RETURN (container freight).',
  })
  @IsOptional()
  @IsInt()
  @Min(0)
  @Transform(({ value }) => Number(value))
  returnQuantity?: number;

  @ApiProperty({ type: [CreateContainerUnitDto] })
  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => CreateContainerUnitDto)
  units!: CreateContainerUnitDto[];
}

export class CreateBulkLineDto {
  @ApiPropertyOptional({ format: 'uuid' })
  @IsOptional()
  @IsUUID()
  cargoTypeId?: string | null;

  @ApiPropertyOptional({ minimum: 0 })
  @IsOptional()
  @IsNumber()
  @Min(0)
  @Transform(({ value }) => Number(value))
  cargoWeightTons?: number;

  @ApiPropertyOptional({ minimum: 0 })
  @IsOptional()
  @IsInt()
  @Min(0)
  @Transform(({ value }) => Number(value))
  itemCount?: number;

  @ApiPropertyOptional({ minimum: 0 })
  @IsOptional()
  @IsInt()
  @Min(0)
  @Transform(({ value }) => Number(value))
  hazardousQuantity?: number;

  @ApiPropertyOptional({ minimum: 0 })
  @IsOptional()
  @IsInt()
  @Min(0)
  @Transform(({ value }) => Number(value))
  reeferQuantity?: number;
}

/** Shipment booking created under a contract (Path A customer, Path B GL ET). */
export class CreateBookingUnderContractDto {
  @ApiPropertyOptional({
    format: 'uuid',
    description: 'Required for GENERAL multi-route contracts; ONE_TIME auto-selected.',
  })
  @IsOptional()
  @IsUUID()
  contractRouteId?: string;

  /**
   * The contract quotes in USD; the customer picks the billing currency here.
   * Omitted → the contract's own currency (USD for contracts created under the
   * current rule, the grandfathered currency for older ones). Intercity is
   * forced to ETB by the service regardless of what is sent.
   */
  @ApiPropertyOptional({
    enum: PAYMENT_CURRENCIES,
    description: 'Billing currency for this shipment. Intercity is always ETB.',
  })
  @IsOptional()
  @IsIn([...PAYMENT_CURRENCIES])
  paymentCurrency?: string;

  @ApiPropertyOptional({
    description:
      'Binding shipment day. Omitted for intercity (DOMESTIC) bookings — staff assign a passing train later.',
    example: '2026-07-15',
  })
  @IsOptional()
  @IsDateString()
  scheduledDate?: string;

  @ApiPropertyOptional({
    enum: SHIPMENT_EQUIPMENT_RETURNS,
    description:
      'Per-shipment equipment return override; omitted → the contract default applies.',
  })
  @IsOptional()
  @IsIn([...SHIPMENT_EQUIPMENT_RETURNS])
  equipmentReturn?: string;

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

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

  @ApiPropertyOptional({
    description: 'What the containers carry — captured per booking (container freight).',
  })
  @IsOptional()
  @IsString()
  cargoFreeText?: string;

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