import { Controller, Get, Query, HttpCode, HttpStatus, Logger, UsePipes, ValidationPipe, UseGuards, } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiResponse, ApiBadRequestResponse, ApiInternalServerErrorResponse, ApiBearerAuth, } from '@nestjs/swagger'; import { PortSearchRequestDto, PortSearchResponseDto } from '../dto/port.dto'; import { PortMapper } from '../mappers/port.mapper'; import { PortSearchService } from '@domain/services/port-search.service'; import { JwtAuthGuard } from '../guards/jwt-auth.guard'; import { CurrentUser, UserPayload } from '../decorators/current-user.decorator'; @ApiTags('Ports') @Controller('ports') @ApiBearerAuth() export class PortsController { private readonly logger = new Logger(PortsController.name); constructor(private readonly portSearchService: PortSearchService) {} @Get('search') @UseGuards(JwtAuthGuard) @HttpCode(HttpStatus.OK) @UsePipes(new ValidationPipe({ transform: true, whitelist: true })) @ApiOperation({ summary: 'Search ports (autocomplete)', description: 'Search for maritime ports by name, city, or UN/LOCODE code. Returns up to 50 results ordered by relevance. Requires authentication.', }) @ApiResponse({ status: HttpStatus.OK, description: 'Port search completed successfully', type: PortSearchResponseDto, }) @ApiResponse({ status: 401, description: 'Unauthorized - missing or invalid token', }) @ApiBadRequestResponse({ description: 'Invalid request parameters', schema: { example: { statusCode: 400, message: ['query must be a string'], error: 'Bad Request', }, }, }) @ApiInternalServerErrorResponse({ description: 'Internal server error', }) async searchPorts( @Query() dto: PortSearchRequestDto, @CurrentUser() user: UserPayload ): Promise { const startTime = Date.now(); this.logger.log( `[User: ${user.email}] Searching ports: query="${dto.query}", limit=${dto.limit || 10}, country=${dto.countryFilter || 'all'}` ); try { // Call domain service const result = await this.portSearchService.search({ query: dto.query, limit: dto.limit, countryFilter: dto.countryFilter, }); const duration = Date.now() - startTime; this.logger.log( `[User: ${user.email}] Port search completed: ${result.totalMatches} results in ${duration}ms` ); // Map to response DTO return PortMapper.toSearchResponseDto(result.ports, result.totalMatches); } catch (error: any) { const duration = Date.now() - startTime; this.logger.error( `[User: ${user.email}] Port search failed after ${duration}ms: ${error?.message || 'Unknown error'}`, error?.stack ); throw error; } } }