import {
  Controller,
  Post,
  Get,
  Body,
  HttpCode,
  HttpStatus,
  Logger,
  UsePipes,
  ValidationPipe,
  UseGuards,
} from '@nestjs/common';
import {
  ApiTags,
  ApiOperation,
  ApiResponse,
  ApiBadRequestResponse,
  ApiInternalServerErrorResponse,
  ApiBearerAuth,
} from '@nestjs/swagger';
import { RateSearchRequestDto, RateSearchResponseDto } from '../dto';
import { RateQuoteMapper } from '../mappers';
import { RateSearchService } from '@domain/services/rate-search.service';
import { CsvRateSearchService } from '@domain/services/csv-rate-search.service';
import { JwtAuthGuard } from '../guards/jwt-auth.guard';
import { CurrentUser, UserPayload } from '../decorators/current-user.decorator';
import { CsvRateSearchDto, CsvRateSearchResponseDto } from '../dto/csv-rate-search.dto';
import { AvailableCompaniesDto, FilterOptionsDto } from '../dto/csv-rate-upload.dto';
import { CsvRateMapper } from '../mappers/csv-rate.mapper';

@ApiTags('Rates')
@Controller('rates')
@ApiBearerAuth()
export class RatesController {
  private readonly logger = new Logger(RatesController.name);

  constructor(
    private readonly rateSearchService: RateSearchService,
    private readonly csvRateSearchService: CsvRateSearchService,
    private readonly csvRateMapper: CsvRateMapper
  ) {}

  @Post('search')
  @UseGuards(JwtAuthGuard)
  @HttpCode(HttpStatus.OK)
  @UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
  @ApiOperation({
    summary: 'Search shipping rates',
    description:
      'Search for available shipping rates from multiple carriers. Results are cached for 15 minutes. Requires authentication.',
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'Rate search completed successfully',
    type: RateSearchResponseDto,
  })
  @ApiResponse({
    status: 401,
    description: 'Unauthorized - missing or invalid token',
  })
  @ApiBadRequestResponse({
    description: 'Invalid request parameters',
    schema: {
      example: {
        statusCode: 400,
        message: ['Origin must be a valid 5-character UN/LOCODE (e.g., NLRTM)'],
        error: 'Bad Request',
      },
    },
  })
  @ApiInternalServerErrorResponse({
    description: 'Internal server error',
  })
  async searchRates(
    @Body() dto: RateSearchRequestDto,
    @CurrentUser() user: UserPayload
  ): Promise<RateSearchResponseDto> {
    const startTime = Date.now();
    this.logger.log(
      `[User: ${user.email}] Searching rates: ${dto.origin} → ${dto.destination}, ${dto.containerType}`
    );

    try {
      // Convert DTO to domain input
      const searchInput = {
        origin: dto.origin,
        destination: dto.destination,
        containerType: dto.containerType,
        mode: dto.mode,
        departureDate: new Date(dto.departureDate),
        quantity: dto.quantity,
        weight: dto.weight,
        volume: dto.volume,
        isHazmat: dto.isHazmat,
        imoClass: dto.imoClass,
      };

      // Execute search
      const result = await this.rateSearchService.execute(searchInput);

      // Convert domain entities to DTOs
      const quoteDtos = RateQuoteMapper.toDtoArray(result.quotes);

      const responseTimeMs = Date.now() - startTime;
      this.logger.log(`Rate search completed: ${quoteDtos.length} quotes, ${responseTimeMs}ms`);

      return {
        quotes: quoteDtos,
        count: quoteDtos.length,
        origin: dto.origin,
        destination: dto.destination,
        departureDate: dto.departureDate,
        containerType: dto.containerType,
        mode: dto.mode,
        fromCache: false, // TODO: Implement cache detection
        responseTimeMs,
      };
    } catch (error: any) {
      this.logger.error(`Rate search failed: ${error?.message || 'Unknown error'}`, error?.stack);
      throw error;
    }
  }

  /**
   * Search CSV-based rates with advanced filters
   */
  @Post('search-csv')
  @UseGuards(JwtAuthGuard)
  @HttpCode(HttpStatus.OK)
  @UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
  @ApiOperation({
    summary: 'Search CSV-based rates with advanced filters',
    description:
      'Search for rates from CSV-loaded carriers (SSC, ECU, TCC, NVO) with advanced filtering options including volume, weight, pallets, price range, transit time, and more.',
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'CSV rate search completed successfully',
    type: CsvRateSearchResponseDto,
  })
  @ApiResponse({
    status: 401,
    description: 'Unauthorized - missing or invalid token',
  })
  @ApiBadRequestResponse({
    description: 'Invalid request parameters',
  })
  async searchCsvRates(
    @Body() dto: CsvRateSearchDto,
    @CurrentUser() user: UserPayload
  ): Promise<CsvRateSearchResponseDto> {
    const startTime = Date.now();
    this.logger.log(
      `[User: ${user.email}] Searching CSV rates: ${dto.origin} → ${dto.destination}, ${dto.volumeCBM} CBM, ${dto.weightKG} kg`
    );

    try {
      // Map DTO to domain input
      const searchInput = {
        origin: dto.origin,
        destination: dto.destination,
        volumeCBM: dto.volumeCBM,
        weightKG: dto.weightKG,
        palletCount: dto.palletCount ?? 0,
        containerType: dto.containerType,
        filters: this.csvRateMapper.mapFiltersDtoToDomain(dto.filters),

        // Service requirements for detailed pricing
        hasDangerousGoods: dto.hasDangerousGoods ?? false,
        requiresSpecialHandling: dto.requiresSpecialHandling ?? false,
        requiresTailgate: dto.requiresTailgate ?? false,
        requiresStraps: dto.requiresStraps ?? false,
        requiresThermalCover: dto.requiresThermalCover ?? false,
        hasRegulatedProducts: dto.hasRegulatedProducts ?? false,
        requiresAppointment: dto.requiresAppointment ?? false,
      };

      // Execute CSV rate search
      const result = await this.csvRateSearchService.execute(searchInput);

      // Map domain output to response DTO
      const response = this.csvRateMapper.mapSearchOutputToResponseDto(result);

      const responseTimeMs = Date.now() - startTime;
      this.logger.log(
        `CSV rate search completed: ${response.totalResults} results, ${responseTimeMs}ms`
      );

      return response;
    } catch (error: any) {
      this.logger.error(
        `CSV rate search failed: ${error?.message || 'Unknown error'}`,
        error?.stack
      );
      throw error;
    }
  }

  /**
   * Get available companies
   */
  @Get('companies')
  @UseGuards(JwtAuthGuard)
  @HttpCode(HttpStatus.OK)
  @ApiOperation({
    summary: 'Get available carrier companies',
    description: 'Returns list of all available carrier companies in the CSV rate system.',
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'List of available companies',
    type: AvailableCompaniesDto,
  })
  async getCompanies(): Promise<AvailableCompaniesDto> {
    this.logger.log('Fetching available companies');

    try {
      const companies = await this.csvRateSearchService.getAvailableCompanies();

      return {
        companies,
        total: companies.length,
      };
    } catch (error: any) {
      this.logger.error(
        `Failed to fetch companies: ${error?.message || 'Unknown error'}`,
        error?.stack
      );
      throw error;
    }
  }

  /**
   * Get filter options
   */
  @Get('filters/options')
  @UseGuards(JwtAuthGuard)
  @HttpCode(HttpStatus.OK)
  @ApiOperation({
    summary: 'Get available filter options',
    description:
      'Returns available options for all filters (companies, container types, currencies).',
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'Available filter options',
    type: FilterOptionsDto,
  })
  async getFilterOptions(): Promise<FilterOptionsDto> {
    this.logger.log('Fetching filter options');

    try {
      const [companies, containerTypes] = await Promise.all([
        this.csvRateSearchService.getAvailableCompanies(),
        this.csvRateSearchService.getAvailableContainerTypes(),
      ]);

      return {
        companies,
        containerTypes,
        currencies: ['USD', 'EUR'],
      };
    } catch (error: any) {
      this.logger.error(
        `Failed to fetch filter options: ${error?.message || 'Unknown error'}`,
        error?.stack
      );
      throw error;
    }
  }
}
