import {
  Controller,
  Get,
  Post,
  Body,
  Param,
  Query,
  HttpStatus,
  HttpCode,
  ParseIntPipe,
  Req,
  Res,
  UseGuards,
  UsePipes,
  ValidationPipe,
} from '@nestjs/common';
import { Response } from 'express';
import {
  ApiTags,
  ApiOperation,
  ApiResponse,
  ApiParam,
  ApiBody,
  ApiBearerAuth,
  ApiSecurity,
} from '@nestjs/swagger';
import { QrAttendanceService } from './qr-attendance.service';
import { GenerateQrDto } from './dto/generate-qr.dto';
import { GenerateBulkQrDto } from './dto/generate-bulk.dto';
import { ScanQrDto } from './dto/scan-qr.dto';
import { ManualCheckinDto } from './dto/manual-checkin.dto';
import { BulkManualCheckinDto } from './dto/bulk-manual-checkin.dto';
import { GetAttendanceListDto } from './dto/get-attendance-list.dto';
import { UndoCheckinDto } from './dto/undo-checkin.dto';
import { AuthenticatedRequest } from 'src/common/interfaces/authenticated-request.interface';
import { ResponseService } from 'src/common/response-handling/response-handler';
import { AppLoggerService } from 'src/common/services/logger.service';
import { handleControllerError } from 'src/common/utils/controller-response-handling';
import { CombinedAuthGuard } from 'src/auth/combined-auth.guard';
import { RolesGuard } from 'src/common/guards/roles.guard';
import { Roles } from 'src/common/decorators/roles.decorator';
import { qrAttendanceConstMessages, SWAGGER_API_RESPONSE } from 'src/common/constants/strings-constants';

@ApiTags('qr-attendance')
@Controller('qr-attendance')
@UseGuards(CombinedAuthGuard, RolesGuard)
@ApiBearerAuth('Authorization')
@ApiSecurity('userIdAuth')
@ApiSecurity('activeRoleAuth')
export class QrAttendanceController {
  constructor(
    private readonly qrAttendanceService: QrAttendanceService,
    private readonly responseService: ResponseService,
    private readonly logger: AppLoggerService,
  ) {}

  // ─── Generate QR ─────────────────────────────────────────────────────────────

  @Post('generate')
  @HttpCode(HttpStatus.CREATED)
  @Roles('admin','shoba','relational_manager','operational_manager')
  @UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
  @ApiOperation({ summary: 'Generate QR for a registration (mirrors HDB genProRegQr)' })
  @ApiBody({ type: GenerateQrDto })
  @ApiResponse({ status: HttpStatus.CREATED, description: SWAGGER_API_RESPONSE.CREATED })
  async generateQr(@Body() dto: GenerateQrDto, @Req() req: any, @Res() res: Response) {
    this.logger.log(qrAttendanceConstMessages.GENERATE_QR_REQUEST, dto);
    try {
      dto.createdBy = req.user?.id;
      dto.updatedBy = req.user?.id;
      const data = await this.qrAttendanceService.generateQr(dto);
      return this.responseService.success(res, qrAttendanceConstMessages.QR_GENERATED, data, HttpStatus.CREATED);
    } catch (error) {
      return handleControllerError(res, error);
    }
  }

  // ─── Generate QR (bulk) ───────────────────────────────────────────────────────

  @Post('generate-bulk')
  @HttpCode(HttpStatus.ACCEPTED)
  @Roles('admin','shoba','relational_manager','operational_manager')
  @UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
  @ApiOperation({ summary: 'Start bulk QR generation for all eligible registrations — returns jobId immediately, processes in background' })
  @ApiBody({ type: GenerateBulkQrDto })
  @ApiResponse({ status: HttpStatus.ACCEPTED, description: 'Returns jobId to poll for status' })
  async generateBulkQr(@Body() dto: GenerateBulkQrDto, @Res() res: Response) {
    this.logger.log(qrAttendanceConstMessages.BULK_QR_START, dto);
    try {
      const data = await this.qrAttendanceService.startBulkGeneration(dto);
      return this.responseService.success(res, qrAttendanceConstMessages.BULK_QR_STARTED, data, HttpStatus.ACCEPTED);
    } catch (error) {
      return handleControllerError(res, error);
    }
  }

  @Get('generate-bulk/status/:jobId')
  @Roles('admin','shoba','relational_manager','operational_manager')
  @ApiOperation({ summary: 'Poll bulk QR generation job status' })
  @ApiParam({ name: 'jobId', type: Number })
  @ApiResponse({ status: HttpStatus.OK, description: 'Returns job progress' })
  @ApiResponse({ status: HttpStatus.NOT_FOUND, description: 'Job not found' })
  async getBulkJobStatus(@Param('jobId', ParseIntPipe) jobId: number, @Res() res: Response) {
    try {
      const data = await this.qrAttendanceService.getBulkJobStatus(jobId);
      return this.responseService.success(res, qrAttendanceConstMessages.BULK_QR_STATUS_FETCHED, data);
    } catch (error) {
      return handleControllerError(res, error);
    }
  }

  // ─── Scan QR ─────────────────────────────────────────────────────────────────

  @Post('scan')
  @HttpCode(HttpStatus.OK)
  @Roles('admin','shoba','relational_manager','operational_manager')
  @UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
  @ApiOperation({ summary: 'Scan QR to mark attendance (mirrors HDB markAttendance)' })
  @ApiBody({ type: ScanQrDto })
  @ApiResponse({ status: HttpStatus.OK, description: 'Attendance marked' })
  @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: 'Invalid seq number' })
  async scanQr(@Body() dto: ScanQrDto, @Req() req: any, @Res() res: Response) {
    this.logger.log(qrAttendanceConstMessages.CHECKIN_REQUEST);
    try {
      dto.checkedInByUserId = req.user?.id;
      const data = await this.qrAttendanceService.scanQr(dto);
      const message = data?.isAttended && data?.checkedInAt
        ? qrAttendanceConstMessages.CHECKIN_SUCCESS
        : qrAttendanceConstMessages.ALREADY_ATTENDED;
      return this.responseService.success(res, message, data);
    } catch (error) {
      return handleControllerError(res, error);
    }
  }

  // ─── Manual Check-in ─────────────────────────────────────────────────────────

  @Post('manual-checkin')
  @HttpCode(HttpStatus.OK)
  @Roles('admin','shoba','relational_manager','operational_manager')
  @UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
  @ApiOperation({ summary: 'Manually mark attendance for a registration' })
  @ApiBody({ type: ManualCheckinDto })
  @ApiResponse({ status: HttpStatus.OK, description: 'Attendance marked' })
  async manualCheckin(@Body() dto: ManualCheckinDto, @Req() req: any, @Res() res: Response) {
    this.logger.log(qrAttendanceConstMessages.CHECKIN_REQUEST, dto);
    try {
      dto.checkedInByUserId = req.user?.id;
      dto.roles = req.user?.roles;
      const data = await this.qrAttendanceService.manualCheckin(dto);
      return this.responseService.success(res, qrAttendanceConstMessages.MANUAL_CHECKIN_SUCCESS, data);
    } catch (error) {
      return handleControllerError(res, error);
    }
  }

  // ─── Undo Check-in ───────────────────────────────────────────────────────────

  @Post('bulk-manual-checkin')
  @HttpCode(HttpStatus.OK)
  @Roles('admin','shoba','relational_manager','operational_manager')
  @UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
  @ApiOperation({ summary: 'Manually mark attendance for multiple records in one call' })
  @ApiBody({ type: BulkManualCheckinDto })
  @ApiResponse({ status: HttpStatus.OK, description: 'Bulk attendance marked' })
  async bulkManualCheckin(@Body() dto: BulkManualCheckinDto, @Req() req: AuthenticatedRequest, @Res() res: Response) {
    this.logger.log(qrAttendanceConstMessages.BULK_MANUAL_CHECKIN_REQUEST, dto);
    try {
      dto.checkedInByUserId = req.user?.id;
      dto.roles = req.user?.roles;
      const data = await this.qrAttendanceService.bulkManualCheckin(dto);
      return this.responseService.success(
        res,
        qrAttendanceConstMessages.BULK_MANUAL_CHECKIN_SUCCESS,
        data,
      );
    } catch (error) {
      return handleControllerError(res, error);
    }
  }

  @Post('undo-checkin')
  @HttpCode(HttpStatus.OK)
  @Roles('admin','shoba','relational_manager','operational_manager' )
  @UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
  @ApiOperation({ summary: 'Undo check-in for one or more attendance records (mirrors HDB undoUserCheckIn)' })
  @ApiBody({ type: UndoCheckinDto })
  @ApiResponse({ status: HttpStatus.OK, description: 'Check-in reverted' })
  async undoCheckin(@Body() dto: UndoCheckinDto, @Res() res: Response) {
    try {
      await this.qrAttendanceService.undoCheckin(dto);
      return this.responseService.success(res, qrAttendanceConstMessages.UNDO_CHECKIN_SUCCESS);
    } catch (error) {
      return handleControllerError(res, error);
    }
  }

  // ─── Queries ─────────────────────────────────────────────────────────────────

  @Get()
  @Roles('admin','shoba','relational_manager','operational_manager')
  @UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
  @ApiOperation({ summary: 'List attendance records with filters (mirrors HDB qrProgramRegistrationSearchListing)' })
  @ApiResponse({ status: HttpStatus.OK, description: SWAGGER_API_RESPONSE.OK })
  async findList(@Query() query: GetAttendanceListDto, @Res() res: Response) {
    try {
      const data = await this.qrAttendanceService.findList(query);
      return this.responseService.success(res, qrAttendanceConstMessages.ATTENDANCE_LIST_FETCHED, data);
    } catch (error) {
      return handleControllerError(res, error);
    }
  }

  @Get(':id')
  @Roles('admin','shoba','relational_manager','operational_manager')
  @ApiOperation({ summary: 'Get an attendance record by ID' })
  @ApiParam({ name: 'id', type: Number })
  @ApiResponse({ status: HttpStatus.OK, description: SWAGGER_API_RESPONSE.OK })
  @ApiResponse({ status: HttpStatus.NOT_FOUND, description: SWAGGER_API_RESPONSE.NOT_FOUND })
  async findById(@Param('id', ParseIntPipe) id: number, @Res() res: Response) {
    try {
      const data = await this.qrAttendanceService.findById(id);
      return this.responseService.success(res, qrAttendanceConstMessages.ATTENDANCE_FETCHED, data);
    } catch (error) {
      return handleControllerError(res, error);
    }
  }
}
