import { Body, Controller, Post, Get, Query } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse, ApiQuery } from '@nestjs/swagger'; import { IsPublic } from '@tria-plc/api-common/modules/auth/decorators/public.decorator'; import { SearchService } from './search.service'; import { SearchTripsDto, FareQuoteDto, FareBreakdownRequestDto } from './search.dto'; @ApiTags('Search') @Controller('search') @IsPublic() export class SearchController { constructor(private service: SearchService) {} @Post() @ApiOperation({ summary: 'Search trips by origin, destination, date, passengers, and nationality', description: `Finds all train schedules matching search criteria with real-time seat availability and coach type options. **Coach Type Selection Flow:** - Users browse available coach types (Economy, VIP, etc.) - Each coach type displays available seat classes and base fares - Users select a coach type to proceed to seat selection - At seat selection, users choose specific seat and class (actual price confirmed here) - Final fare may adjust based on seat position/amenities selected **Features:** - Any origin→destination stop pair (not just terminals) - Age-based passenger counts (adults ≥5 years, children <5 years) - Nationality filtering (Ethiopian, Djiboutian, Other) - Real-time seat availability per class - Multi-currency fare display - Segment-based availability (seat booked A→B still available B→D)` }) @ApiResponse({ status: 200, description: 'Matching schedules with coachTypes array showing available coach types with seat classes and base fares' }) searchTrips(@Body() dto: SearchTripsDto) { return this.service.searchTrips(dto); } @Post('fare-quote') @ApiOperation({ summary: 'Get fare quote with age-based pricing and multi-currency support', description: `Calculates detailed fare breakdown for a specific schedule leg. Age-Based Pricing: - ADULT (≥5 years): 100% of base fare - CHILD (<5 years): First child FREE, subsequent children 100% - Example: 2 adults + 3 children = 4× base fare Pricing Rules (priority order): 1. Schedule-scoped FareRule (tripId = scheduleId) 2. Segment route FareRule (e.g. ADD-DRE) 3. Full-route FareRule (e.g. ADD-DJI) 4. Default hardcoded fare Multi-Currency: - Transaction currency: ETB - Display currencies: ETB, DJF, USD - Real-time exchange rate conversion Nationality-Based: - Ethiopian: National ID verification required - Djiboutian: Passport details, Waafi payment available - Other: Passport details, international payments` }) @ApiResponse({ status: 200, description: 'Fare breakdown with adult/child pricing, discounts, taxes, and currency conversion' }) @ApiResponse({ status: 404, description: 'Schedule not found or origin/destination not on schedule' }) getFareQuote(@Body() dto: FareQuoteDto) { return this.service.getFareQuote(dto); } @Get('fare-breakdown') @ApiOperation({ summary: 'Per-passenger fare breakdown for booking review page', description: `Calculates a line-item fare for each individual passenger based on their date of birth, nationality, and chosen seat class. - Age is derived from dateOfBirth at request time (ADULT ≥5 yrs, CHILD <5 yrs) - First CHILD in the list travels free (pays only premium + insurance fees) - Each passenger can have a different seat class and nationality - Returns per-passenger lines plus subtotal, discount, and grand total **passengers** must be a URL-encoded JSON array, e.g.: \`[{"passengerName":"Abebe","dateOfBirth":"1985-03-15","seatClassId":"uuid","nationality":"Ethiopian"}]\``, }) @ApiQuery({ name: 'scheduleId', description: 'TrainSchedule UUID' }) @ApiQuery({ name: 'originStationId', description: 'Origin station UUID' }) @ApiQuery({ name: 'destinationStationId', description: 'Destination station UUID' }) @ApiQuery({ name: 'passengers', description: 'URL-encoded JSON array of passengers: [{passengerName, dateOfBirth, seatClassId, nationality?}]' }) @ApiQuery({ name: 'promoCode', required: false }) @ApiQuery({ name: 'displayCurrency', required: false, enum: ['ETB', 'DJF', 'USD'] }) @ApiResponse({ status: 200, description: 'Per-passenger fare lines with grand total' }) @ApiResponse({ status: 404, description: 'Schedule not found' }) getFareBreakdown(@Query() dto: FareBreakdownRequestDto) { return this.service.getFareBreakdown(dto); } }