import { IsString, IsOptional, IsEnum, IsInt, Min } from 'class-validator'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { Currency } from '@prisma/client'; // Nationality → home currency mapping export const NATIONALITY_CURRENCY_MAP: Record = { Ethiopian: Currency.ETB, Djiboutian: Currency.DJF, }; export function resolveCurrencyFromNationality(nationality?: string): Currency { if (!nationality) return Currency.ETB; return NATIONALITY_CURRENCY_MAP[nationality] ?? Currency.USD; } export class FareCalculateDto { @ApiProperty({ example: 'route-uuid', description: 'Route UUID — used to look up stop distances' }) @IsString() routeId: string; @ApiProperty({ example: 'station-uuid', description: 'Origin station UUID (must be a stop on the route)' }) @IsString() originStationId: string; @ApiProperty({ example: 'station-uuid', description: 'Destination station UUID (must come after origin)' }) @IsString() destinationStationId: string; @ApiProperty({ example: 'seat-class-uuid', description: 'SeatClass UUID — its basePrice is the per-km rate in ETB minor units' }) @IsString() seatClassId: string; @ApiPropertyOptional({ example: 'Ethiopian', description: 'Passenger nationality. Determines the billing currency: Ethiopian → ETB, Djiboutian → DJF, other → USD. Defaults to ETB.', }) @IsOptional() @IsString() nationality?: string; @ApiPropertyOptional({ example: 2, description: 'Number of adult passengers (age ≥ 5). Defaults to 1.', }) @IsOptional() @Type(() => Number) @IsInt() @Min(1) adultCount?: number; @ApiPropertyOptional({ example: 1, description: 'Number of child passengers (age < 5). First child travels free.', }) @IsOptional() @Type(() => Number) @IsInt() @Min(0) childCount?: number; @ApiPropertyOptional({ example: 'WEEKEND15', description: 'Promo code for discount' }) @IsOptional() @IsString() promoCode?: string; @ApiPropertyOptional({ example: 'schedule-uuid', description: 'Schedule UUID — used to match schedule-scoped FareRules first' }) @IsOptional() @IsString() scheduleId?: string; } export class FareBreakdownDto { @ApiProperty({ example: 'ADD-DJI' }) routeCode: string; @ApiProperty({ example: 'Addis Ababa' }) originName: string; @ApiProperty({ example: 'Djibouti' }) destinationName: string; @ApiProperty({ example: 'Economy Regular' }) seatClassName: string; @ApiProperty({ example: 756 }) totalDistanceKm: number; @ApiProperty({ example: 120 }) ratePerKmMinor: number; @ApiProperty({ example: 90720 }) baseFarePerPassengerMinor: number; @ApiProperty({ example: 2 }) adultCount: number; @ApiProperty({ example: 1 }) childCount: number; @ApiProperty({ example: 1 }) freeChildrenCount: number; @ApiProperty({ example: 0 }) paidChildrenCount: number; @ApiProperty({ example: 181440 }) subtotalMinor: number; @ApiProperty({ example: 0 }) discountMinor: number; @ApiProperty({ example: 9072 }) taxMinor: number; @ApiProperty({ example: 190512 }) totalMinor: number; @ApiProperty({ example: 'ETB', enum: Currency }) billingCurrency: Currency; @ApiProperty({ example: 190512 }) totalInBillingCurrency: number; @ApiProperty({ example: 3.25 }) exchangeRate: number; @ApiProperty({ description: 'Step-by-step calculation trace for transparency' }) calculation: string; }