import {
  Controller,
  Get,
  Post,
  Patch,
  Put,
  Delete,
  Param,
  Body,
  ParseIntPipe,
  Query,
  UploadedFile,
  UseInterceptors,
  BadRequestException,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { diskStorage } from 'multer';
import { extname } from 'path';
import { Public } from './../common/guards/decorators/public.decorator';
import { TrainingCertificatesService } from './training-certificates.service';
import { CreateTrainingCertificateDto } from './dto/create-training-certificate.dto';
import { UpdateTrainingCertificateDto } from './dto/update-training-certificate.dto';
import { BatchCreateTrainingCertificatesDto } from './dto/batch-create-training-certificates.dto';
import { TrainingCertificate } from './entities/training-certificate.entity';

@Controller('training-certificates')
export class TrainingCertificatesController {
  constructor(private readonly service: TrainingCertificatesService) {}

  // ── Create ONE training certificate ───────────────────────────
  @Post()
  async create(@Body() dto: CreateTrainingCertificateDto) {
    return this.service.create(dto);
  }

  // ── Create a WHOLE SESSION (many participants at once) ────────
  @Post('batch')
  async batchCreate(@Body() dto: BatchCreateTrainingCertificatesDto) {
    return this.service.batchCreate(dto);
  }

  // ── UNIFIED PUBLIC VERIFY (training + company certificates) ───
  // Point the verify page / all QR codes here. Must stay ABOVE ':id'.
  // @Public() = excluded from the global JwtAuthGuard (same pattern
  // as src/meeting/join.controller.ts). Anyone scanning a QR code has
  // no account — the token itself is the credential.
  @Public()
  @Get('verify')
  async verify(
    @Query('token') token: string,
    @Query('fp') fingerprint?: string,
  ) {
    return this.service.verify(token, fingerprint);
  }

  // ── PUBLIC MANUAL VERIFY (certificate no + participant name) ──
  // Used by the website's "Verify Manually" form. Requires BOTH the
  // exact certificate number AND a matching participant name, and
  // only returns the same safe fields as the QR verify endpoint.
  // Must also stay ABOVE ':id'.
  @Public()
  @Get('verify-manual')
  async verifyManual(
    @Query('certNo') certNo: string,
    @Query('name') participantName: string,
  ) {
    return this.service.verifyManual(certNo, participantName);
  }

  // ── List with pagination + search ──────────────────────────────
  @Get()
  async findAll(
    @Query('page', new ParseIntPipe({ optional: true })) page = 1,
    @Query('search') search?: string,
  ) {
    const {
      data,
      total,
      page: currentPage,
      lastPage,
    } = await this.service.findAll(page, search);

    const dataWithQr = await Promise.all(
      data.map(async (cert) => ({
        ...cert,
        verification_url: this.service.buildVerificationUrl(cert),
        qrCode: await this.service.generateQrCode(
          cert.qrcode_token,
          cert.fingerprint,
          cert.verification_domain as 'local' | 'international',
        ),
      })),
    );

    return { data: dataWithQr, total, page: currentPage, lastPage };
  }

  // ── Single certificate (+ QR regenerated dynamically) ──────────
  @Get(':id')
  async findOne(@Param('id', ParseIntPipe) id: number) {
    const certificate = await this.service.findOne(id);

    const qrCode = await this.service.generateQrCode(
      certificate.qrcode_token,
      certificate.fingerprint,
      certificate.verification_domain as 'local' | 'international',
    );

    return {
      certificate,
      verification_url: this.service.buildVerificationUrl(certificate),
      qrCode,
    };
  }

  @Patch(':id')
  async updatePartial(
    @Param('id', ParseIntPipe) id: number,
    @Body() dto: Partial<UpdateTrainingCertificateDto>,
  ): Promise<TrainingCertificate> {
    return this.service.updatePartial(id, dto);
  }

  @Put(':id')
  async update(
    @Param('id', ParseIntPipe) id: number,
    @Body() dto: UpdateTrainingCertificateDto,
  ): Promise<TrainingCertificate> {
    return this.service.update(id, dto);
  }

  @Delete(':id')
  async remove(
    @Param('id', ParseIntPipe) id: number,
  ): Promise<{ message: string }> {
    return this.service.remove(id);
  }

  // ── Upload the signed PHYSICAL SCAN (JPG / PNG / PDF) ──────────
  // First upload flips status pending_scan → active
  @Post(':id/scan')
  @UseInterceptors(
    FileInterceptor('scan', {
      storage: diskStorage({
        destination: './uploads/training-certificate-scans',
        filename: (req, file, cb) =>
          cb(
            null,
            `training-cert-${req.params.id}-${Date.now()}${
              extname(file.originalname) || '.jpg'
            }`,
          ),
      }),
      limits: { fileSize: 10 * 1024 * 1024 },
      fileFilter: (req, file, cb) => {
        const ok = /image\/(jpe?g|png)|application\/pdf/.test(file.mimetype);
        cb(ok ? null : new BadRequestException('Only JPG, PNG or PDF allowed'), ok);
      },
    }),
  )
  async uploadScan(
    @Param('id', ParseIntPipe) id: number,
    @UploadedFile() file: Express.Multer.File,
    @Query('uploaded_by') uploadedBy?: string,
  ) {
    if (!file) {
      throw new BadRequestException('No file uploaded (field: scan)');
    }
    const base = process.env.PUBLIC_BASE_URL || 'https://crm.qrsyst.com';
    const fileUrl = `${base}/uploads/training-certificate-scans/${file.filename}`;
    return this.service.attachScan(
      id,
      fileUrl,
      uploadedBy ? parseInt(uploadedBy, 10) : undefined,
    );
  }
}
