import { IsString, IsArray, ValidateNested, IsOptional, IsEnum } from 'class-validator'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Type } from 'class-transformer'; export enum JourneyDirection { ONE_WAY = 'ONE_WAY', OUTBOUND = 'OUTBOUND', RETURN = 'RETURN' } export class PassengerSeatDto { @ApiProperty({ example: 'passenger-uuid', description: 'Passenger UUID' }) @IsString() passengerId: string; @ApiProperty({ example: 'seat-uuid', description: 'Seat UUID assigned to this passenger' }) @IsString() seatId: string; } export class HoldSeatsDto { @ApiProperty({ example: 'schedule-uuid', description: 'TrainSchedule UUID' }) @IsString() scheduleId: string; @ApiProperty({ example: 'station-uuid', description: 'Origin station UUID — scopes the hold to a leg so the seat can be reused on non-overlapping legs' }) @IsString() originStationId: string; @ApiProperty({ example: 'station-uuid', description: 'Destination station UUID for this leg' }) @IsString() destinationStationId: string; @ApiPropertyOptional({ enum: JourneyDirection, example: JourneyDirection.OUTBOUND, description: 'Journey direction for round-trip bookings. ONE_WAY for single journeys, OUTBOUND/RETURN for round-trip legs. Allows same seats to be held for different directions.' }) @IsOptional() @IsEnum(JourneyDirection) journeyDirection?: JourneyDirection; @ApiProperty({ type: [PassengerSeatDto], description: 'One entry per passenger. Each passenger is assigned exactly one seat. Duplicate passengerId or seatId within the same request is rejected.', example: [ { passengerId: 'passenger-uuid-1', seatId: 'seat-uuid-1' }, { passengerId: 'passenger-uuid-2', seatId: 'seat-uuid-2' }, ], }) @IsArray() @ValidateNested({ each: true }) @Type(() => PassengerSeatDto) passengers: PassengerSeatDto[]; } export class ReleaseHoldDto { @ApiProperty({ example: 'hold-uuid', description: 'SeatHold UUID to release' }) @IsString() holdId: string; } /** Coarse bucket for *why* a seat was pulled out of sale — mirrors the Prisma * `SeatBlockReasonCategory` enum. The free-text `reason` stays the detail. */ export enum SeatBlockReasonCategory { MAINTENANCE = 'MAINTENANCE', VIP_RESERVED = 'VIP_RESERVED', SAFETY = 'SAFETY', OPERATIONAL = 'OPERATIONAL', OTHER = 'OTHER', } export class BlockSeatDto { @ApiProperty({ example: 'Torn upholstery — awaiting replacement', description: 'Free-text detail explaining the block. Shown verbatim in the revenue-loss report.', }) @IsString() reason: string; @ApiPropertyOptional({ example: 'schedule-uuid', description: 'When set, the block applies only to this schedule. Omit for a global block that pulls the seat out of sale on every schedule its coach runs on.', }) @IsOptional() @IsString() scheduleId?: string; @ApiPropertyOptional({ enum: SeatBlockReasonCategory, default: SeatBlockReasonCategory.OTHER, description: 'Reporting bucket for this block. Defaults to OTHER. Drives the reason-category breakdown in the Blocked Seat Revenue Loss report.', }) @IsOptional() @IsEnum(SeatBlockReasonCategory) reasonCategory?: SeatBlockReasonCategory; } export class SetMaintenanceDto { @ApiProperty({ example: 'Seat recline mechanism jammed' }) @IsString() reason: string; }