import { Body, Controller, Get, Param, Post, Query, UseGuards, Delete, Patch, SetMetadata } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiBearerAuth, ApiBody, ApiQuery } from '@nestjs/swagger';
import { TicketsService } from './tickets.service';
import { PassengerStaff, PassengerAdmin } from '../../common/passenger-guards';
import { PASSENGER_PERMS } from '../../seed/passenger-permissions.registry';

@ApiTags('Tickets')
@Controller('tickets')
export class TicketsController {
  constructor(private service: TicketsService) {}
  
  @Post('generate-missing')
  @PassengerStaff(PASSENGER_PERMS.tickets.generate)
  @ApiBearerAuth('IAM-auth')
  @ApiOperation({
    summary: 'Generate tickets for all confirmed bookings that are missing them',
    description: 'Finds every CONFIRMED booking with no ticket rows and attempts to generate tickets for each. Returns a summary of processed/generated/failed/remaining counts. Call repeatedly until remaining=0.',
  })
  @ApiQuery({ name: 'limit', required: false, description: 'Max bookings to process per call (default 10)' })
  generateMissing(@Query('limit') limit?: string) {
    return this.service.generateMissing(limit ? parseInt(limit, 10) : 10);
  }

  @Post('smart-assign/:bookingId')
  @PassengerStaff(PASSENGER_PERMS.tickets.generate)
  @ApiBearerAuth('IAM-auth')
  @ApiOperation({
    summary: 'Smart seat assignment + ticket generation',
    description:
      'Keeps original seats if still free, auto-reassigns to an available seat of the same coach type if taken, ' +
      'or throws 409 if the schedule is fully booked in that class.',
  })
  smartAssignAndGenerate(@Param('bookingId') bookingId: string) {
    return this.service.smartAssignAndGenerate(bookingId);
  }

  @Post('generate/:bookingId')
  @PassengerStaff(PASSENGER_PERMS.tickets.generate)
  @ApiBearerAuth('IAM-auth')
  @ApiOperation({
    summary: 'Generate ticket for booking',
    description: 'Creates a ticket for a confirmed booking with succeeded payment. Requires payment to be SUCCEEDED and booking to be CONFIRMED.'
  })
  generateTicket(@Param('bookingId') bookingId: string) {
    return this.service.generate(bookingId);
  }

  @Patch('update-seats/:bookingId')
  @SetMetadata('isPublic', true)
  @ApiOperation({
    summary: 'Update ticket seats before final confirmation',
    description: 'Allows users to change selected seats after ticket generation. Removes old seat blocks and creates new ones for updated seats.'
  })
  updateSeats(@Param('bookingId') bookingId: string, @Body() body: { seatIds: string[] }) {
    return this.service.updateSeats(bookingId, body.seatIds);
  }
  
  @Get()
  @PassengerStaff(PASSENGER_PERMS.tickets.generate)
  @ApiBearerAuth('IAM-auth')
  @ApiOperation({ summary: 'List all tickets with optional filters' })
  @ApiQuery({ name: 'search', required: false })
  @ApiQuery({ name: 'status', required: false })
  @ApiQuery({ name: 'originStationId', required: false })
  @ApiQuery({ name: 'destinationStationId', required: false })
  @ApiQuery({ name: 'arrivalDate', required: false })
  @ApiQuery({ name: 'departureDate', required: false })
  @ApiQuery({ name: 'dateFrom', required: false })
  @ApiQuery({ name: 'dateTo', required: false })
  @ApiQuery({ name: 'coachId', required: false })
  @ApiQuery({ name: 'skip', required: false })
  @ApiQuery({ name: 'take', required: false })
  listTickets(
    @Query('search') search?: string,
    @Query('status') status?: string,
    @Query('originStationId') originStationId?: string,
    @Query('destinationStationId') destinationStationId?: string,
    @Query('arrivalDate') arrivalDate?: string,
    @Query('departureDate') departureDate?: string,
    @Query('dateFrom') dateFrom?: string,
    @Query('dateTo') dateTo?: string,
    @Query('coachId') coachId?: string,
    @Query('skip') skip?: string,
    @Query('take') take?: string,
  ) {
    return this.service.listTickets({
      search,
      status,
      originStationId,
      destinationStationId,
      arrivalDate,
      departureDate,
      dateFrom,
      dateTo,
      coachId,
      skip: skip ? parseInt(skip) : 0,
      take: take ? parseInt(take) : 50,
    });
  }
  
  @Get('by-order/:merchantOrderId')
  @PassengerStaff(PASSENGER_PERMS.tickets.generate)
  @ApiBearerAuth('IAM-auth')
  @ApiOperation({
    summary: 'Get ticket by merchant order ID',
    description: 'Looks up the booking ID from the PaymentIntent using merchantOrderId, then returns the full ticket information.'
  })
  getByMerchantOrderId(@Param('merchantOrderId') merchantOrderId: string) {
    return this.service.getByMerchantOrderId(merchantOrderId);
  }

  @Get(':bookingRef')
  @SetMetadata('isPublic', true)
  @ApiOperation({ summary: 'Get ticket with QR code and passenger details (public)' })
  getByRef(@Param('bookingRef') ref: string) { 
    return this.service.getByRef(ref); 
  }
  
  @Post('scan-board/:qrCodeOrRef')
  @PassengerStaff(PASSENGER_PERMS.tickets.generate)
  @ApiBearerAuth('IAM-auth')
  @ApiOperation({
    summary: 'Scan QR code or booking ref and automatically board ticket',
    description: 'Scans ticket QR code or booking reference and automatically boards the passenger. Handles errors like expired tickets, already used tickets, etc. Designed for mobile boarding interface.'
  })
  @ApiBody({
    schema: {
      type: 'object',
      required: ['validatorId'],
      properties: {
        validatorId: { type: 'string', example: 'agent-uuid' },
        gateId: { type: 'string', example: 'gate-01' },
      },
    },
  })
  scanAndBoard(
    @Param('qrCodeOrRef') qrCodeOrRef: string,
    @Body('validatorId') validatorId: string,
    @Body('gateId') gateId?: string,
  ) {
    return this.service.scanAndBoard(qrCodeOrRef, validatorId, gateId);
  }

  @Post(':bookingRef/validate')
  @PassengerStaff(PASSENGER_PERMS.tickets.generate)
  @ApiBearerAuth('IAM-auth')
  @ApiOperation({ 
    summary: 'Validate ticket at gate with audit logging',
    description: 'Validates ticket QR/barcode at station gate. For round-trip bookings, supply `leg` (OUTBOUND or RETURN) to record which leg is being used. Defaults to OUTBOUND if omitted. Records validation in audit log with timestamp, gate, and validator.'
  })
  @ApiBody({
    schema: {
      type: 'object',
      required: ['validatorId'],
      properties: {
        validatorId: { type: 'string', example: 'agent-uuid' },
        gateId:      { type: 'string', example: 'gate-01' },
        leg: {
          type: 'string',
          enum: ['OUTBOUND', 'RETURN', 'LEG1', 'LEG2', 'OUTBOUND_LEG1', 'OUTBOUND_LEG2', 'RETURN_LEG1', 'RETURN_LEG2'],
          description: 'ONE_WAY: omit | TRANSIT: LEG1/LEG2 | ROUND_TRIP: OUTBOUND/RETURN | ROUND_TRIP_TRANSIT: OUTBOUND_LEG1/OUTBOUND_LEG2/RETURN_LEG1/RETURN_LEG2',
        },
      },
    },
  })
  validate(
    @Param('bookingRef') ref: string,
    @Body('validatorId') validatorId: string,
    @Body('gateId') gateId?: string,
    @Body('leg') leg?: string,
  ) {
    return this.service.validate(ref, validatorId, gateId, leg);
  }
  
  @Get(':ticketId/validation-logs')
  @PassengerStaff(PASSENGER_PERMS.tickets.generate)
  @ApiBearerAuth('IAM-auth')
  @ApiOperation({ summary: 'Get validation logs for ticket' })
  getValidationLogs(@Param('ticketId') ticketId: string) {
    return this.service.getValidationLogs(ticketId);
  }

  @Get('offline/export')
  @PassengerStaff(PASSENGER_PERMS.tickets.generate)
  @ApiBearerAuth('IAM-auth')
  @ApiOperation({ summary: 'Export tickets for offline validation' })
  exportOfflineData(@Query('scheduleId') scheduleId: string) {
    return this.service.exportOfflineData(scheduleId);
  }

  @Post('validate/offline')
  @PassengerStaff(PASSENGER_PERMS.tickets.generate)
  @ApiBearerAuth('IAM-auth')
  @ApiOperation({
    summary: 'Batch import offline validations',
    description: 'Processes validations collected offline. Each entry may include an optional `leg` field (OUTBOUND | RETURN) for round-trip tickets. Deduplication is per bookingRef+leg combination so both legs of the same booking can be submitted in one batch.'
  })
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        validations: {
          type: 'array',
          items: {
            type: 'object',
            required: ['bookingRef', 'validatorId', 'validatedAt'],
            properties: {
              bookingRef:  { type: 'string' },
              validatorId: { type: 'string' },
              gateId:      { type: 'string' },
              validatedAt: { type: 'string', format: 'date-time' },
              leg: { type: 'string', enum: ['OUTBOUND', 'RETURN', 'LEG1', 'LEG2', 'OUTBOUND_LEG1', 'OUTBOUND_LEG2', 'RETURN_LEG1', 'RETURN_LEG2'] },
            },
          },
        },
      },
    },
  })
  validateOfflineBatch(@Body() body: { validations: any[] }) {
    return this.service.validateOfflineBatch(body.validations);
  }

  @Delete(':id')
  @PassengerAdmin()
  @ApiBearerAuth('IAM-auth')
  @ApiOperation({
    summary: 'Delete ticket (admin only)',
    description: 'Permanently deletes a ticket record and removes associated seat blocks'
  })
  delete(@Param('id') id: string) {
    return this.service.delete(id);
  }

  @Patch(':id/restore')
  @PassengerStaff(PASSENGER_PERMS.tickets.generate)
  @ApiBearerAuth('IAM-auth')
  @ApiOperation({ summary: 'Restore a cancelled ticket by resetting its status to ACTIVE' })
  restore(@Param('id') id: string) {
    return this.service.restore(id);
  }
}
