import { Body, Controller, Post, Get, Query } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiQuery, ApiResponse } from '@nestjs/swagger'; import { ConfigService } from '@nestjs/config'; import { FareEngineService } from './fare-engine.service'; import { FareCalculateDto, FareBreakdownDto } from './fare-engine.dto'; import { FaydaConfig } from '../../config/fayda.config'; @ApiTags('Fare Engine') @Controller('fare-engine') export class FareEngineController { constructor( private service: FareEngineService, private configService: ConfigService, ) {} @Post('calculate') @ApiOperation({ summary: 'Calculate fare for a journey leg', description: `Computes fare using the formula: **Fare = totalKm × ratePerKm × exchangeRate** - \`totalKm\` — sum of \`distanceKm\` on RouteStop records between origin and destination - \`ratePerKm\` — \`SeatClass.basePrice\` (stored in ETB minor units per km) - \`exchangeRate\` — derived from passenger nationality: - **Ethiopian** → ETB (rate = 1.0) - **Djiboutian** → DJF (rate ≈ 3.25) - **Other / unspecified** → USD (rate ≈ 0.018) Age-based pricing: first child (age < 5) travels free, subsequent children pay full fare. 5% tax applied after promo discount. Returns a full breakdown including a human-readable calculation trace.`, }) @ApiResponse({ status: 201, type: FareBreakdownDto, description: 'Full fare breakdown with calculation trace' }) @ApiResponse({ status: 400, description: 'Invalid route/station combination or missing distanceKm on route stops' }) @ApiResponse({ status: 404, description: 'Route or seat class not found' }) calculate(@Body() dto: FareCalculateDto) { return this.service.calculate(dto); } @Get('compare') @ApiOperation({ summary: 'Compare fares across all seat classes for a route leg', description: 'Returns fare breakdown for every active seat class on the requested leg. Useful for rendering a class-selection table on the booking screen.', }) @ApiQuery({ name: 'routeId', description: 'Route UUID' }) @ApiQuery({ name: 'originStationId', description: 'Origin station UUID' }) @ApiQuery({ name: 'destinationStationId', description: 'Destination station UUID' }) @ApiQuery({ name: 'nationality', required: false, description: 'Passenger nationality (Ethiopian | Djiboutian | other). Determines billing currency.' }) @ApiQuery({ name: 'adultCount', required: false, type: Number, description: 'Number of adults (default 1)' }) @ApiQuery({ name: 'childCount', required: false, type: Number, description: 'Number of children (default 0)' }) @ApiResponse({ status: 200, description: 'Array of fare breakdowns, one per active seat class, ordered by price ascending' }) compareClasses( @Query('routeId') routeId: string, @Query('originStationId') originStationId: string, @Query('destinationStationId') destinationStationId: string, @Query('nationality') nationality?: string, @Query('adultCount') adultCount?: string, @Query('childCount') childCount?: string, ) { return this.service.compareClasses( routeId, originStationId, destinationStationId, nationality, adultCount ? parseInt(adultCount) : 1, childCount ? parseInt(childCount) : 0, ); } } @ApiTags('Config') @Controller('config') export class ConfigController { constructor(private configService: ConfigService) {} @Get('fayda-status') @ApiOperation({ summary: 'Check Verifayda 2.0 configuration status', description: 'Returns whether Verifayda integration is enabled and ready to use' }) @ApiResponse({ status: 200, description: 'Verifayda status retrieved successfully', schema: { example: { enabled: true, mode: 'production', apiUrl: 'https://api.verifayda.gov.et/v2' } } }) getFaydaStatus() { const faydaConfig = this.configService.get('fayda'); const verifaydaEnabled = this.configService.get('VERIFAYDA_ENABLED', false); return { enabled: faydaConfig?.enabled || verifaydaEnabled, mode: verifaydaEnabled ? 'production' : 'development', apiUrl: this.configService.get('VERIFAYDA_API_URL', 'https://api.verifayda.gov.et/v2'), }; } }