import { Body, Controller, Get, Post, Put, Delete, Param, UseGuards, Request, Query } from '@nestjs/common'; import { ApiTags, ApiBearerAuth, ApiOperation, ApiResponse, ApiParam, ApiQuery } from '@nestjs/swagger'; import { ConfigurableFareService } from './configurable-fare.service'; import { CreateFareConfigurationDto, UpdateFareConfigurationDto, FareTestScenarioDto, FareCalculationResultDto, MigrateLegacyDto, CreateNewFormulaDto, ToggleFeatureDto } from './configurable-fare.dto'; import { IamGuard } from '../../common/iam-adapter'; import { Roles } from '../../common/roles.decorator'; @ApiTags('Configurable Fares') @Controller('admin/fare-configurations') @ApiBearerAuth('IAM-auth') @UseGuards(IamGuard) @Roles('ADMIN') export class ConfigurableFareController { constructor(private service: ConfigurableFareService) {} @Get() @ApiOperation({ summary: 'List all fare configurations' }) @ApiResponse({ status: 200, description: 'List of all configurations with summary counts' }) async getAllConfigurations() { return this.service.getAllConfigurations(); } @Post() @ApiOperation({ summary: 'Create new fare configuration' }) @ApiResponse({ status: 201, description: 'Configuration created successfully' }) @ApiResponse({ status: 400, description: 'Invalid configuration data' }) async createConfiguration( @Body() dto: CreateFareConfigurationDto, @Request() req: any ) { const createdBy = req.user?.id || req.user?.sub; return this.service.createConfiguration(dto, createdBy); } @Get(':id') @ApiOperation({ summary: 'Get configuration details' }) @ApiParam({ name: 'id', description: 'Configuration ID' }) @ApiResponse({ status: 200, description: 'Configuration details with all rules' }) @ApiResponse({ status: 404, description: 'Configuration not found' }) async getConfigurationById(@Param('id') id: string) { return this.service.getConfigurationById(id); } @Put(':id') @ApiOperation({ summary: 'Update configuration' }) @ApiParam({ name: 'id', description: 'Configuration ID' }) @ApiResponse({ status: 200, description: 'Configuration updated successfully' }) @ApiResponse({ status: 404, description: 'Configuration not found' }) async updateConfiguration( @Param('id') id: string, @Body() dto: UpdateFareConfigurationDto, @Request() req: any ) { const updatedBy = req.user?.id || req.user?.sub; return this.service.updateConfiguration(id, dto, updatedBy); } @Post(':id/activate') @ApiOperation({ summary: 'Activate configuration' }) @ApiParam({ name: 'id', description: 'Configuration ID' }) @ApiResponse({ status: 200, description: 'Configuration activated successfully' }) @ApiResponse({ status: 404, description: 'Configuration not found' }) async activateConfiguration(@Param('id') id: string, @Request() req: any) { const activatedBy = req.user?.id || req.user?.sub; return this.service.activateConfiguration(id, activatedBy); } @Delete(':id') @ApiOperation({ summary: 'Delete configuration' }) @ApiParam({ name: 'id', description: 'Configuration ID' }) @ApiResponse({ status: 200, description: 'Configuration deleted successfully' }) @ApiResponse({ status: 404, description: 'Configuration not found' }) @ApiResponse({ status: 409, description: 'Cannot delete active configuration' }) async deleteConfiguration(@Param('id') id: string, @Request() req: any) { const deletedBy = req.user?.id || req.user?.sub; return this.service.deleteConfiguration(id, deletedBy); } @Post(':id/test') @ApiOperation({ summary: 'Test fare calculation with configuration' }) @ApiParam({ name: 'id', description: 'Configuration ID' }) @ApiResponse({ status: 200, type: FareCalculationResultDto, description: 'Fare calculation result' }) @ApiResponse({ status: 400, description: 'Invalid test scenario or missing rate rules' }) async testConfiguration( @Param('id') id: string, @Body() scenario: FareTestScenarioDto ): Promise { return this.service.testConfiguration(id, scenario); } @Get(':id/audit') @ApiOperation({ summary: 'Get configuration audit trail' }) @ApiParam({ name: 'id', description: 'Configuration ID' }) @ApiResponse({ status: 200, description: 'Audit trail entries' }) async getAuditTrail(@Param('id') id: string) { return this.service.getAuditTrail(id); } } @ApiTags('Configurable Fares') @Controller('admin/fare-migration') @ApiBearerAuth('IAM-auth') @UseGuards(IamGuard) @Roles('ADMIN') export class FareMigrationController { constructor(private service: ConfigurableFareService) {} @Post('migrate-legacy') @ApiOperation({ summary: 'Migrate existing fare rules to configurable system' }) @ApiResponse({ status: 200, description: 'Migration completed successfully' }) @ApiResponse({ status: 400, description: 'Migration failed' }) async migrateLegacySystem(@Body() dto: MigrateLegacyDto) { return this.service.migrateLegacySystem(dto); } @Post('create-new-formula') @ApiOperation({ summary: 'Create new formula configuration with defaults' }) @ApiResponse({ status: 201, description: 'New formula configuration created' }) async createNewFormulaConfiguration(@Body() dto: CreateNewFormulaDto) { return this.service.createNewFormulaConfiguration(dto); } @Post('complete-setup') @ApiOperation({ summary: 'Complete system setup (migrate + create + activate)', description: 'Performs full system migration and setup in one operation' }) @ApiResponse({ status: 200, description: 'System setup completed successfully' }) async completeSetup(@Body() body: { activateNewFormula?: boolean; enableFeature?: boolean }) { // Step 1: Migrate legacy system const migrationResult = await this.service.migrateLegacySystem({ dryRun: false }); // Step 2: Create new formula configuration const newConfig = await this.service.createNewFormulaConfiguration({ name: 'Default System Configuration', description: 'System-generated configuration with optimal defaults', activateImmediately: body.activateNewFormula !== false }); // Step 3: Enable feature flag if requested if (body.enableFeature) { await this.service.toggleFeature({ featureName: 'USE_CONFIGURABLE_FARES', enabled: true, config: { rollout_percentage: 100 } }); } return { migration: migrationResult, newConfiguration: newConfig, featureEnabled: body.enableFeature || false, message: 'System setup completed successfully' }; } @Get('status') @ApiOperation({ summary: 'Get migration and setup status' }) @ApiResponse({ status: 200, description: 'Current system status' }) async getStatus() { const featureStatus = await this.service.getFeatureStatus('USE_CONFIGURABLE_FARES'); const configurations = await this.service.getAllConfigurations(); const activeConfig = (configurations as any[]).find(config => config.is_active); return { configurableFaresEnabled: featureStatus.enabled, rolloutPercentage: featureStatus.config?.rollout_percentage || 0, totalConfigurations: (configurations as any[]).length, activeConfiguration: activeConfig?.id || null, activeConfigurationName: activeConfig?.name || null, systemReady: featureStatus.enabled && !!activeConfig }; } } @ApiTags('Configurable Fares') @Controller('admin/fare-configurations/system') @ApiBearerAuth('IAM-auth') @UseGuards(IamGuard) @Roles('ADMIN') export class FareSystemController { constructor(private service: ConfigurableFareService) {} @Get('feature-status') @ApiOperation({ summary: 'Check configurable fares feature status' }) @ApiQuery({ name: 'feature', required: false, description: 'Feature name (defaults to USE_CONFIGURABLE_FARES)' }) @ApiResponse({ status: 200, description: 'Feature status retrieved' }) async getFeatureStatus(@Query('feature') featureName = 'USE_CONFIGURABLE_FARES') { return this.service.getFeatureStatus(featureName); } @Post('toggle-feature') @ApiOperation({ summary: 'Enable or disable configurable fares system' }) @ApiResponse({ status: 200, description: 'Feature toggled successfully' }) async toggleFeature(@Body() dto: ToggleFeatureDto) { return this.service.toggleFeature(dto); } @Post('enable-configurable-fares') @ApiOperation({ summary: 'Enable configurable fares with rollout percentage', description: 'Quick endpoint to enable the configurable fares feature' }) @ApiResponse({ status: 200, description: 'Configurable fares enabled successfully' }) async enableConfigurableFares(@Body() body: { rolloutPercentage?: number }) { return this.service.toggleFeature({ featureName: 'USE_CONFIGURABLE_FARES', enabled: true, config: { rollout_percentage: body.rolloutPercentage || 100 } }); } @Post('disable-configurable-fares') @ApiOperation({ summary: 'Disable configurable fares (fallback to legacy system)', description: 'Disables the configurable fares feature and falls back to legacy fare calculation' }) @ApiResponse({ status: 200, description: 'Configurable fares disabled successfully' }) async disableConfigurableFares() { return this.service.toggleFeature({ featureName: 'USE_CONFIGURABLE_FARES', enabled: false, config: { rollout_percentage: 0 } }); } }