import { Body, Controller, Delete, Get, Param, Patch, Post, Query, UseGuards } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiBearerAuth, ApiParam, ApiQuery, ApiBody, ApiResponse } from '@nestjs/swagger'; import { FleetService } from './fleet.service'; import { CreateTrainDto, CreateCoachDto, UpdateCoachDto, AssignCoachDto, ListCoachesDto, CreateCoachTypeDto, UpdateCoachTypeDto, CreateClassDto, UpdateClassDto, GenerateSeatMapDto } from './fleet.dto'; import { JwtGuard } from '../../common/jwt.guard'; @ApiTags('Fleet') @Controller('fleet') @UseGuards(JwtGuard) @ApiBearerAuth('JWT-auth') export class FleetController { constructor(private service: FleetService) {} // Coach Type Endpoints @Get('coach-types') @ApiOperation({ summary: 'List all coach types' }) @ApiResponse({ status: 200, description: 'Array of coach types' }) getCoachTypes() { return this.service.getCoachTypes(); } @Post('coach-types') @ApiOperation({ summary: 'Create a coach type' }) @ApiBody({ type: CreateCoachTypeDto }) @ApiResponse({ status: 201, description: 'Coach type created' }) createCoachType(@Body() dto: CreateCoachTypeDto) { return this.service.createCoachType(dto); } @Patch('coach-types/:id') @ApiOperation({ summary: 'Update a coach type' }) @ApiParam({ name: 'id', description: 'Coach Type UUID' }) @ApiBody({ type: UpdateCoachTypeDto }) @ApiResponse({ status: 200, description: 'Coach type updated' }) @ApiResponse({ status: 404, description: 'Coach type not found' }) updateCoachType(@Param('id') id: string, @Body() dto: UpdateCoachTypeDto) { return this.service.updateCoachType(id, dto); } @Delete('coach-types/:id') @ApiOperation({ summary: 'Delete a coach type' }) @ApiParam({ name: 'id', description: 'Coach Type UUID' }) @ApiResponse({ status: 200, description: 'Coach type deleted' }) @ApiResponse({ status: 404, description: 'Coach type not found' }) deleteCoachType(@Param('id') id: string) { return this.service.deleteCoachType(id); } // Class Endpoints @Get('classes') @ApiOperation({ summary: 'List all classes' }) @ApiQuery({ name: 'coachTypeId', required: false, description: 'Filter by coach type' }) @ApiResponse({ status: 200, description: 'Array of classes' }) getClasses(@Query('coachTypeId') coachTypeId?: string) { return this.service.getClasses(coachTypeId); } @Post('classes') @ApiOperation({ summary: 'Create a class' }) @ApiBody({ type: CreateClassDto }) @ApiResponse({ status: 201, description: 'Class created' }) createClass(@Body() dto: CreateClassDto) { return this.service.createClass(dto); } @Patch('classes/:id') @ApiOperation({ summary: 'Update a class' }) @ApiParam({ name: 'id', description: 'Class UUID' }) @ApiBody({ type: UpdateClassDto }) @ApiResponse({ status: 200, description: 'Class updated' }) @ApiResponse({ status: 404, description: 'Class not found' }) updateClass(@Param('id') id: string, @Body() dto: UpdateClassDto) { return this.service.updateClass(id, dto); } @Delete('classes/:id') @ApiOperation({ summary: 'Delete a class' }) @ApiParam({ name: 'id', description: 'Class UUID' }) @ApiResponse({ status: 200, description: 'Class deleted' }) @ApiResponse({ status: 404, description: 'Class not found' }) deleteClass(@Param('id') id: string) { return this.service.deleteClass(id); } // Seat Class Endpoints (DEPRECATED - use Classes endpoints instead) @Get('seat-classes') @ApiOperation({ summary: 'List all classes (DEPRECATED - use /fleet/classes)' }) @ApiQuery({ name: 'coachTypeId', required: false, description: 'Filter by coach type' }) @ApiResponse({ status: 200, description: 'Array of classes' }) getSeatClasses(@Query('coachTypeId') coachTypeId?: string) { return this.service.getClasses(coachTypeId); } @Post('seat-classes') @ApiOperation({ summary: 'Create a class (DEPRECATED - use /fleet/classes)' }) @ApiBody({ type: CreateClassDto }) @ApiResponse({ status: 201, description: 'Class created' }) createSeatClass(@Body() dto: CreateClassDto) { return this.service.createClass(dto); } @Patch('seat-classes/:id') @ApiOperation({ summary: 'Update a class (DEPRECATED - use /fleet/classes)' }) @ApiParam({ name: 'id', description: 'Class UUID' }) @ApiBody({ type: UpdateClassDto }) @ApiResponse({ status: 200, description: 'Class updated' }) @ApiResponse({ status: 404, description: 'Class not found' }) updateSeatClass(@Param('id') id: string, @Body() dto: UpdateClassDto) { return this.service.updateClass(id, dto); } @Delete('seat-classes/:id') @ApiOperation({ summary: 'Delete a class (DEPRECATED - use /fleet/classes)' }) @ApiParam({ name: 'id', description: 'Class UUID' }) @ApiResponse({ status: 200, description: 'Class deleted' }) @ApiResponse({ status: 404, description: 'Class not found' }) deleteSeatClass(@Param('id') id: string) { return this.service.deleteClass(id); } // Train Endpoints @Get('trains') @ApiOperation({ summary: 'List all trains with their recent schedules' }) @ApiResponse({ status: 200, description: 'Array of trains' }) getTrains() { return this.service.getTrains(); } @Post('trains') @ApiOperation({ summary: 'Create a train service' }) @ApiBody({ type: CreateTrainDto }) @ApiResponse({ status: 201, description: 'Train created' }) createTrain(@Body() dto: CreateTrainDto) { return this.service.createTrain(dto); } @Patch('trains/:id') @ApiOperation({ summary: 'Update a train service' }) @ApiParam({ name: 'id', description: 'Train UUID' }) @ApiBody({ type: CreateTrainDto }) @ApiResponse({ status: 200, description: 'Train updated' }) @ApiResponse({ status: 404, description: 'Train not found' }) updateTrain(@Param('id') id: string, @Body() dto: CreateTrainDto) { return this.service.updateTrain(id, dto); } @Delete('trains/:id') @ApiOperation({ summary: 'Delete a train service' }) @ApiParam({ name: 'id', description: 'Train UUID' }) @ApiResponse({ status: 200, description: 'Train deleted' }) @ApiResponse({ status: 404, description: 'Train not found' }) deleteTrain(@Param('id') id: string) { return this.service.deleteTrain(id); } @Patch('trains/:id/restore') @ApiOperation({ summary: 'Restore (reactivate) a deactivated train' }) @ApiParam({ name: 'id', description: 'Train UUID' }) @ApiResponse({ status: 200, description: 'Train restored' }) @ApiResponse({ status: 404, description: 'Train not found' }) restoreTrain(@Param('id') id: string) { return this.service.restoreTrain(id); } // Coach Endpoints @Get('coaches') @ApiOperation({ summary: 'List coaches with seat status summary' }) @ApiQuery({ name: 'status', required: false, description: 'Filter by status: ACTIVE, INACTIVE' }) @ApiQuery({ name: 'scheduleId', required: false, description: 'Filter coaches assigned to schedule' }) @ApiResponse({ status: 200, description: 'Array of coaches', schema: { example: [ { id: '550e8400-e29b-41d4-a716-446655440000', number: 'A-001', sequence: 1, coachTypeId: 'coach-type-uuid', coachType: { id: 'coach-type-uuid', code: 'sleeper', name: 'Sleeper Coach' }, arrangement: '2+2', capacity: 60, status: 'ACTIVE', totalSeats: 60, availableSeats: 45, occupiedSeats: 15, blockedSeats: 0, createdAt: '2024-01-15T10:30:00.000Z', updatedAt: '2024-01-15T10:30:00.000Z' } ] } }) listCoaches( @Query('status') status?: string, @Query('scheduleId') scheduleId?: string, ) { const dto: ListCoachesDto = { status, scheduleId, }; return this.service.listCoaches(dto); } @Get('coaches/:id') @ApiOperation({ summary: 'Get single coach with seat layout' }) @ApiParam({ name: 'id', description: 'Coach UUID' }) @ApiResponse({ status: 200, description: 'Coach detail with seats by row', schema: { example: { id: '550e8400-e29b-41d4-a716-446655440000', number: 'A-001', sequence: 1, coachTypeId: 'coach-type-uuid', coachType: { id: 'coach-type-uuid', code: 'sleeper', name: 'Sleeper Coach' }, arrangement: '2+2', capacity: 60, status: 'ACTIVE', seats: [ { id: 'seat-uuid-1', seatNumber: '1A', status: 'AVAILABLE', class: { id: 'class-uuid', name: 'Economy', baseFareMinor: 5000 } } ], createdAt: '2024-01-15T10:30:00.000Z', updatedAt: '2024-01-15T10:30:00.000Z' } } }) @ApiResponse({ status: 404, description: 'Coach not found' }) getCoach(@Param('id') id: string) { return this.service.getCoach(id); } @Post('coaches') @ApiOperation({ summary: 'Create a coach with auto-generated seat numbers' }) @ApiBody({ type: CreateCoachDto }) @ApiResponse({ status: 201, description: 'Coach created', schema: { example: { id: '550e8400-e29b-41d4-a716-446655440000', number: 'A-001', sequence: 1, coachTypeId: 'coach-type-uuid', arrangement: '2+2', capacity: 60, status: 'ACTIVE', createdAt: '2024-01-15T10:30:00.000Z', updatedAt: '2024-01-15T10:30:00.000Z' } } }) @ApiResponse({ status: 400, description: 'Invalid arrangement format' }) createCoach(@Body() dto: CreateCoachDto) { return this.service.createCoach(dto); } @Patch('coaches/:id') @ApiOperation({ summary: 'Update coach properties' }) @ApiParam({ name: 'id', description: 'Coach UUID' }) @ApiBody({ type: UpdateCoachDto }) @ApiResponse({ status: 200, description: 'Coach updated', schema: { example: { id: '550e8400-e29b-41d4-a716-446655440000', number: 'A-001', sequence: 1, coachTypeId: 'coach-type-uuid', arrangement: '2+2', capacity: 60, status: 'ACTIVE', createdAt: '2024-01-15T10:30:00.000Z', updatedAt: '2024-01-15T10:30:00.000Z' } } }) @ApiResponse({ status: 404, description: 'Coach not found' }) updateCoach(@Param('id') id: string, @Body() dto: UpdateCoachDto) { return this.service.updateCoach(id, dto); } @Delete('coaches/:id') @ApiOperation({ summary: 'Delete a coach' }) @ApiParam({ name: 'id', description: 'Coach UUID' }) @ApiResponse({ status: 200, description: 'Coach deleted successfully' }) @ApiResponse({ status: 404, description: 'Coach not found' }) deleteCoach(@Param('id') id: string) { return this.service.deleteCoach(id); } @Post('assignments') @ApiOperation({ summary: 'Assign a coach to a schedule' }) @ApiBody({ type: AssignCoachDto }) @ApiResponse({ status: 201, description: 'Coach assigned' }) @ApiResponse({ status: 404, description: 'Schedule or coach not found' }) assignCoach(@Body() dto: AssignCoachDto) { return this.service.assignCoach(dto); } @Delete('assignments/:id') @ApiOperation({ summary: 'Remove a coach assignment' }) @ApiParam({ name: 'id', description: 'Assignment UUID' }) @ApiResponse({ status: 200, description: 'Assignment removed' }) @ApiResponse({ status: 404, description: 'Assignment not found' }) removeAssignment(@Param('id') id: string) { return this.service.removeAssignment(id); } @Post('seatmap/generate') @ApiOperation({ summary: 'Preview bed seat map — ECONOMY_BED or VIP_BED', description: `Generates a structured seat map for bed coaches without persisting anything. **ECONOMY_BED**: 6 beds per room — Left(Lower/Middle/Upper) + Right(Lower/Middle/Upper) **VIP_BED**: 4 beds per room — Left(Lower/Upper) + Right(Lower/Upper) Use this to preview the full flat seat list before creating coaches.`, }) @ApiBody({ type: GenerateSeatMapDto }) @ApiResponse({ status: 201, description: 'Generated seat map preview', schema: { example: { coachCount: 1, roomsPerCoach: 2, roomType: 'ECONOMY_BED', bedsPerRoom: 6, totalBeds: 12, seats: [ { seat_id: 'C1-C1-R1-S1', coach_id: 'C1', room_id: 'C1-R1', category: 'ECONOMY_BED', position: 'LEFT', bed_type: 'LOWER', sequence_number: 1, status: 'AVAILABLE' }, { seat_id: 'C1-C1-R1-S2', coach_id: 'C1', room_id: 'C1-R1', category: 'ECONOMY_BED', position: 'LEFT', bed_type: 'MIDDLE', sequence_number: 2, status: 'AVAILABLE' }, { seat_id: 'C1-C1-R1-S3', coach_id: 'C1', room_id: 'C1-R1', category: 'ECONOMY_BED', position: 'LEFT', bed_type: 'UPPER', sequence_number: 3, status: 'AVAILABLE' }, { seat_id: 'C1-C1-R1-S4', coach_id: 'C1', room_id: 'C1-R1', category: 'ECONOMY_BED', position: 'RIGHT', bed_type: 'LOWER', sequence_number: 4, status: 'AVAILABLE' }, { seat_id: 'C1-C1-R1-S5', coach_id: 'C1', room_id: 'C1-R1', category: 'ECONOMY_BED', position: 'RIGHT', bed_type: 'MIDDLE', sequence_number: 5, status: 'AVAILABLE' }, { seat_id: 'C1-C1-R1-S6', coach_id: 'C1', room_id: 'C1-R1', category: 'ECONOMY_BED', position: 'RIGHT', bed_type: 'UPPER', sequence_number: 6, status: 'AVAILABLE' }, ], }, }, }) generateSeatMap(@Body() dto: GenerateSeatMapDto) { return this.service.generateSeatMapPreview(dto); } @Get('analytics') @ApiOperation({ summary: 'Fleet analytics and occupancy metrics' }) @ApiResponse({ status: 200, description: 'Occupancy statistics' }) getAnalytics() { return this.service.getAnalytics(); } }