import {
  Controller,
  Post,
  Get,
  Patch,
  Body,
  Param,
  Query,
  UseGuards,
  UseInterceptors,
  UploadedFiles,
  Request,
  BadRequestException,
  ParseIntPipe,
  DefaultValuePipe,
  Res,
  HttpStatus,
} from '@nestjs/common';
import { FilesInterceptor } from '@nestjs/platform-express';
import {
  ApiTags,
  ApiOperation,
  ApiResponse,
  ApiConsumes,
  ApiBody,
  ApiBearerAuth,
  ApiQuery,
  ApiParam,
} from '@nestjs/swagger';
import { Response } from 'express';
import { JwtAuthGuard } from '../guards/jwt-auth.guard';
import { Public } from '../decorators/public.decorator';
import { CsvBookingService } from '../services/csv-booking.service';
import {
  CreateCsvBookingDto,
  CsvBookingResponseDto,
  UpdateCsvBookingStatusDto,
  CsvBookingListResponseDto,
  CsvBookingStatsDto,
} from '../dto/csv-booking.dto';

/**
 * CSV Bookings Controller
 *
 * Handles HTTP requests for CSV-based booking requests
 */
@ApiTags('CSV Bookings')
@Controller('csv-bookings')
export class CsvBookingsController {
  constructor(private readonly csvBookingService: CsvBookingService) {}

  /**
   * Create a new CSV booking request
   *
   * POST /api/v1/csv-bookings
   */
  @Post()
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @UseInterceptors(FilesInterceptor('documents', 10))
  @ApiConsumes('multipart/form-data')
  @ApiOperation({
    summary: 'Create a new CSV booking request',
    description:
      'Creates a new booking request from CSV rate selection. Uploads documents, sends email to carrier, and creates a notification for the user.',
  })
  @ApiBody({
    schema: {
      type: 'object',
      required: [
        'carrierName',
        'carrierEmail',
        'origin',
        'destination',
        'volumeCBM',
        'weightKG',
        'palletCount',
        'priceUSD',
        'priceEUR',
        'primaryCurrency',
        'transitDays',
        'containerType',
      ],
      properties: {
        carrierName: { type: 'string', example: 'SSC Consolidation' },
        carrierEmail: { type: 'string', format: 'email', example: 'bookings@sscconsolidation.com' },
        origin: { type: 'string', example: 'NLRTM' },
        destination: { type: 'string', example: 'USNYC' },
        volumeCBM: { type: 'number', example: 25.5 },
        weightKG: { type: 'number', example: 3500 },
        palletCount: { type: 'number', example: 10 },
        priceUSD: { type: 'number', example: 1850.5 },
        priceEUR: { type: 'number', example: 1665.45 },
        primaryCurrency: { type: 'string', enum: ['USD', 'EUR'], example: 'USD' },
        transitDays: { type: 'number', example: 28 },
        containerType: { type: 'string', example: 'LCL' },
        notes: { type: 'string', example: 'Handle with care' },
        documents: {
          type: 'array',
          items: { type: 'string', format: 'binary' },
          description: 'Shipping documents (Bill of Lading, Packing List, Invoice, etc.)',
        },
      },
    },
  })
  @ApiResponse({
    status: 201,
    description: 'Booking created successfully',
    type: CsvBookingResponseDto,
  })
  @ApiResponse({ status: 400, description: 'Invalid request data or missing documents' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  async createBooking(
    @Body() dto: CreateCsvBookingDto,
    @UploadedFiles() files: Express.Multer.File[],
    @Request() req: any
  ): Promise<CsvBookingResponseDto> {
    // Debug: Log request details
    console.log('=== CSV Booking Request Debug ===');
    console.log('req.user:', req.user);
    console.log('req.body:', req.body);
    console.log('dto:', dto);
    console.log('files:', files?.length);
    console.log('================================');

    if (!files || files.length === 0) {
      throw new BadRequestException('At least one document is required');
    }

    // Validate user authentication
    if (!req.user || !req.user.id) {
      throw new BadRequestException('User authentication failed - no user info in request');
    }

    if (!req.user.organizationId) {
      throw new BadRequestException('Organization ID is required');
    }

    const userId = req.user.id;
    const organizationId = req.user.organizationId;

    // Convert string values to numbers (multipart/form-data sends everything as strings)
    const sanitizedDto: CreateCsvBookingDto = {
      ...dto,
      volumeCBM: typeof dto.volumeCBM === 'string' ? parseFloat(dto.volumeCBM) : dto.volumeCBM,
      weightKG: typeof dto.weightKG === 'string' ? parseFloat(dto.weightKG) : dto.weightKG,
      palletCount:
        typeof dto.palletCount === 'string' ? parseInt(dto.palletCount, 10) : dto.palletCount,
      priceUSD: typeof dto.priceUSD === 'string' ? parseFloat(dto.priceUSD) : dto.priceUSD,
      priceEUR: typeof dto.priceEUR === 'string' ? parseFloat(dto.priceEUR) : dto.priceEUR,
      transitDays:
        typeof dto.transitDays === 'string' ? parseInt(dto.transitDays, 10) : dto.transitDays,
    };

    return await this.csvBookingService.createBooking(sanitizedDto, files, userId, organizationId);
  }

  /**
   * Get a booking by ID
   *
   * GET /api/v1/csv-bookings/:id
   */
  @Get(':id')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @ApiOperation({
    summary: 'Get booking by ID',
    description: 'Retrieve a specific CSV booking by its ID. Only accessible by the booking owner.',
  })
  @ApiParam({ name: 'id', description: 'Booking ID (UUID)' })
  @ApiResponse({
    status: 200,
    description: 'Booking retrieved successfully',
    type: CsvBookingResponseDto,
  })
  @ApiResponse({ status: 404, description: 'Booking not found' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  async getBooking(@Param('id') id: string, @Request() req: any): Promise<CsvBookingResponseDto> {
    const userId = req.user.id;
    return await this.csvBookingService.getBookingById(id, userId);
  }

  /**
   * Get current user's bookings (paginated)
   *
   * GET /api/v1/csv-bookings
   */
  @Get()
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @ApiOperation({
    summary: 'Get user bookings',
    description: 'Retrieve all bookings for the authenticated user with pagination.',
  })
  @ApiQuery({ name: 'page', required: false, type: Number, example: 1 })
  @ApiQuery({ name: 'limit', required: false, type: Number, example: 10 })
  @ApiResponse({
    status: 200,
    description: 'Bookings retrieved successfully',
    type: CsvBookingListResponseDto,
  })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  async getUserBookings(
    @Request() req: any,
    @Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number,
    @Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number
  ): Promise<CsvBookingListResponseDto> {
    const userId = req.user.id;
    return await this.csvBookingService.getUserBookings(userId, page, limit);
  }

  /**
   * Get booking statistics for user
   *
   * GET /api/v1/csv-bookings/stats/me
   */
  @Get('stats/me')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @ApiOperation({
    summary: 'Get user booking statistics',
    description:
      'Get aggregated statistics for the authenticated user (pending, accepted, rejected, cancelled).',
  })
  @ApiResponse({
    status: 200,
    description: 'Statistics retrieved successfully',
    type: CsvBookingStatsDto,
  })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  async getUserStats(@Request() req: any): Promise<CsvBookingStatsDto> {
    const userId = req.user.id;
    return await this.csvBookingService.getUserStats(userId);
  }

  /**
   * Accept a booking request (PUBLIC - token-based)
   *
   * GET /api/v1/csv-bookings/:token/accept
   */
  @Public()
  @Get(':token/accept')
  @ApiOperation({
    summary: 'Accept booking request (public)',
    description:
      'Public endpoint for carriers to accept a booking via email link. Updates booking status and notifies the user.',
  })
  @ApiParam({ name: 'token', description: 'Booking confirmation token (UUID)' })
  @ApiResponse({
    status: 200,
    description: 'Booking accepted successfully. Redirects to confirmation page.',
  })
  @ApiResponse({ status: 404, description: 'Booking not found or invalid token' })
  @ApiResponse({
    status: 400,
    description: 'Booking cannot be accepted (invalid status or expired)',
  })
  async acceptBooking(@Param('token') token: string, @Res() res: Response): Promise<void> {
    const booking = await this.csvBookingService.acceptBooking(token);

    // Redirect to frontend confirmation page
    const frontendUrl = process.env.APP_URL || 'http://localhost:3000';
    res.redirect(
      HttpStatus.FOUND,
      `${frontendUrl}/csv-bookings/${booking.id}/confirmed?action=accepted`
    );
  }

  /**
   * Reject a booking request (PUBLIC - token-based)
   *
   * GET /api/v1/csv-bookings/:token/reject
   */
  @Public()
  @Get(':token/reject')
  @ApiOperation({
    summary: 'Reject booking request (public)',
    description:
      'Public endpoint for carriers to reject a booking via email link. Updates booking status and notifies the user.',
  })
  @ApiParam({ name: 'token', description: 'Booking confirmation token (UUID)' })
  @ApiQuery({
    name: 'reason',
    required: false,
    description: 'Rejection reason',
    example: 'No capacity available',
  })
  @ApiResponse({
    status: 200,
    description: 'Booking rejected successfully. Redirects to confirmation page.',
  })
  @ApiResponse({ status: 404, description: 'Booking not found or invalid token' })
  @ApiResponse({
    status: 400,
    description: 'Booking cannot be rejected (invalid status or expired)',
  })
  async rejectBooking(
    @Param('token') token: string,
    @Query('reason') reason: string,
    @Res() res: Response
  ): Promise<void> {
    const booking = await this.csvBookingService.rejectBooking(token, reason);

    // Redirect to frontend confirmation page
    const frontendUrl = process.env.APP_URL || 'http://localhost:3000';
    res.redirect(
      HttpStatus.FOUND,
      `${frontendUrl}/csv-bookings/${booking.id}/confirmed?action=rejected`
    );
  }

  /**
   * Cancel a booking (user action)
   *
   * PATCH /api/v1/csv-bookings/:id/cancel
   */
  @Patch(':id/cancel')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @ApiOperation({
    summary: 'Cancel booking',
    description: 'Cancel a pending booking. Only accessible by the booking owner.',
  })
  @ApiParam({ name: 'id', description: 'Booking ID (UUID)' })
  @ApiResponse({
    status: 200,
    description: 'Booking cancelled successfully',
    type: CsvBookingResponseDto,
  })
  @ApiResponse({ status: 404, description: 'Booking not found' })
  @ApiResponse({ status: 400, description: 'Booking cannot be cancelled (already accepted)' })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  async cancelBooking(
    @Param('id') id: string,
    @Request() req: any
  ): Promise<CsvBookingResponseDto> {
    const userId = req.user.id;
    return await this.csvBookingService.cancelBooking(id, userId);
  }

  /**
   * Get organization bookings (for managers/admins)
   *
   * GET /api/v1/csv-bookings/organization/all
   */
  @Get('organization/all')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @ApiOperation({
    summary: 'Get organization bookings',
    description:
      "Retrieve all bookings for the user's organization with pagination. For managers/admins.",
  })
  @ApiQuery({ name: 'page', required: false, type: Number, example: 1 })
  @ApiQuery({ name: 'limit', required: false, type: Number, example: 10 })
  @ApiResponse({
    status: 200,
    description: 'Organization bookings retrieved successfully',
    type: CsvBookingListResponseDto,
  })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  async getOrganizationBookings(
    @Request() req: any,
    @Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number,
    @Query('limit', new DefaultValuePipe(10), ParseIntPipe) limit: number
  ): Promise<CsvBookingListResponseDto> {
    const organizationId = req.user.organizationId;
    return await this.csvBookingService.getOrganizationBookings(organizationId, page, limit);
  }

  /**
   * Get organization booking statistics
   *
   * GET /api/v1/csv-bookings/stats/organization
   */
  @Get('stats/organization')
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @ApiOperation({
    summary: 'Get organization booking statistics',
    description: "Get aggregated statistics for the user's organization. For managers/admins.",
  })
  @ApiResponse({
    status: 200,
    description: 'Statistics retrieved successfully',
    type: CsvBookingStatsDto,
  })
  @ApiResponse({ status: 401, description: 'Unauthorized' })
  async getOrganizationStats(@Request() req: any): Promise<CsvBookingStatsDto> {
    const organizationId = req.user.organizationId;
    return await this.csvBookingService.getOrganizationStats(organizationId);
  }
}
