import {
  Controller,
  Get,
  Post,
  Delete,
  Param,
  Query,
  Req,
  Res,
  Body,
  Patch,
  UploadedFile,
  UploadedFiles,
  UseInterceptors,
  StreamableFile,
  BadRequestException,
  Logger,
  UnauthorizedException,
  ParseIntPipe,

} from '@nestjs/common';
import { FileInterceptor, FileFieldsInterceptor } from '@nestjs/platform-express';
import { memoryStorage } from 'multer';
import type { Request, Response } from 'express';

import { PreviousNcService } from '../services/previous-nc.service';
import { PreviousNcPdfService } from '../services/previous-nc-pdf.service';
import { NcClosureService } from '../services/nc-closure.service';
import { ListPreviousNcsDto } from '../dto/list-previous-ncs.dto';
import { UpdatePreviousNcDto } from '../dto/update-previous-nc.dto';
import { AuditNcStatusExcelService } from '../services/audit-nc-status-excel.service';
import { AuditNcStatusPdfService } from '../services/audit-nc-status-pdf.service';
@Controller('previous-nc')
export class PreviousNcController {
  private readonly logger = new Logger('PreviousNcController');

  constructor(
    private readonly previousNcService: PreviousNcService,
    private readonly pdfService: PreviousNcPdfService,
    private readonly closureService: NcClosureService,
    private readonly ncExcel: AuditNcStatusExcelService,
    private readonly ncPdf: AuditNcStatusPdfService,
  ) { }

  // Resolve the user id from the JWT payload.
  private getCurrentUserId(req: Request): number {
    const user = (req as any).user;
    const id = user?.id ?? user?.userId ?? user?.sub;
    if (id === undefined || id === null) {
      throw new BadRequestException(
        'User id not found on request.user (JwtAuthGuard / strategy issue?)',
      );
    }
    return Number(id);
  }

  // ═══════════════════════════════════════════════════════════════
  // EXISTING ENDPOINTS (unchanged)
  // ═══════════════════════════════════════════════════════════════

  // GET /api/previous-nc/paged
  @Get('paged')
  async getPaged(@Query() q: ListPreviousNcsDto, @Req() req: Request) {
    const userId = this.getCurrentUserId(req);
    this.logger.log(
      `GET /previous-nc/paged — user=${userId}, source=${q.source}, status=${q.status}, search="${q.search ?? ''}"`,
    );
    return this.previousNcService.listPaged(q, userId);
  }

  // GET /api/previous-nc/users
  @Get('users')
  async getUsers() {
    return this.previousNcService.listUsers();
  }
  // POST /api/previous-nc/new  — raise a NEW NC from an audit
  @Post('new')
  async createNew(@Body() dto: any, @Req() req: Request) {
    const userId = this.getCurrentUserId(req);
    this.logger.log(
      `POST /previous-nc/new — user=${userId}, audit_id=${dto?.audit_id}`,
    );
    return this.previousNcService.createNew(dto, userId);
  }
  @Post('closures/:source/:id/send-email')
  async resendClosureEmail(
    @Param('source') source: string,
    @Param('id', ParseIntPipe) id: number,
    @Body() body: any,
    @Req() req: Request,
  ) {
    const src = this.validateSource(source);   // 🆕
    const userId = this.getCurrentUserId(req);
    return this.closureService.resendClosureEmail(src, id, {
      closure_to: body.closure_to,
      closure_cc: body.closure_cc,
      closure_bcc: body.closure_bcc,
    }, userId);
  }

  // POST /api/previous-nc/closures/:source/:id/send-evidence
  @Post('closures/:source/:id/send-evidence')
  async sendEvidence(
    @Param('source') source: string,
    @Param('id', ParseIntPipe) id: number,
    @Body() dto: { evidence_to?: string; evidence_cc?: string; evidence_bcc?: string },
    @Req() req: Request,
  ) {
    const src = this.validateSource(source);
    const userId = this.getCurrentUserId(req);
    this.logger.log(
      `POST /previous-nc/closures/${src}/${id}/send-evidence — user=${userId}`,
    );
    return this.closureService.sendEvidenceEmail(src, id, dto, userId);
  }
  // POST /api/previous-nc/:source/:id/send-email

  @Post(':source/:id/send-email')
  async sendNcEmail(
    @Param('source') source: string,
    @Param('id', ParseIntPipe) id: number,
    @Body() dto: any,
    @Req() req: Request,
  ) {
    const src = this.validateSource(source);
    const userId = this.getCurrentUserId(req);
    this.logger.log(
      `POST /previous-nc/${src}/${id}/send-email — user=${userId}`,
    );
    return this.previousNcService.sendNcNotificationManual(
      src,
      id,
      dto,
      userId,
    );
  }
  // ═══════════════════════════════════════════════════════════════
  // 🆕 PHASE 1 — EVIDENCE ENDPOINTS
  // (must come before :source/:id pattern routes)
  // ═══════════════════════════════════════════════════════════════

  // POST /api/previous-nc/:source/:id/entries/:entryId/evidence
  @Post(':source/:id/entries/:entryId/evidence')
  @UseInterceptors(
    FileInterceptor('file', {
      storage: memoryStorage(),
      limits: { fileSize: 25 * 1024 * 1024 }, // 25 MB Multer-side guard
    }),
  )
  async uploadEntryEvidence(
    @Param('source') source: string,
    @Param('id', ParseIntPipe) ncId: number,
    @Param('entryId', ParseIntPipe) entryId: number,
    @UploadedFile() file: Express.Multer.File,
    @Req() req: Request,
  ) {
    if (!file) {
      throw new BadRequestException(
        'No file uploaded (field name must be "file")',
      );
    }
    const src = this.validateSource(source);
    const userId = this.getCurrentUserId(req);

    this.logger.log(
      `POST /previous-nc/${src}/${ncId}/entries/${entryId}/evidence — user=${userId}, file="${file.originalname}", size=${file.size}`,
    );

    return this.previousNcService.uploadEntryEvidence(
      src,
      ncId,
      entryId,
      file,
      userId,
    );
  }
  // DELETE /api/previous-nc/:source/:id/entries/:entryId
  @Delete(':source/:id/entries/:entryId')
  async deleteEntry(
    @Param('source') source: string,
    @Param('id', ParseIntPipe) ncId: number,
    @Param('entryId', ParseIntPipe) entryId: number,
    @Req() req: Request,
  ) {
    const src = this.validateSource(source);
    const userId = this.getCurrentUserId(req);
    this.logger.log(
      `DELETE /previous-nc/${src}/${ncId}/entries/${entryId} — user=${userId}`,
    );
    return this.previousNcService.deleteEntry(src, ncId, entryId, userId);
  }
  // GET /api/previous-nc/:source/:id/entries/:entryId/file
  @Get(':source/:id/entries/:entryId/file')
  async getEntryFile(
    @Param('source') source: string,
    @Param('id', ParseIntPipe) ncId: number,
    @Param('entryId', ParseIntPipe) entryId: number,
    @Query('index') index: string,        // 👈 NEW: optional ?index=0
    @Res({ passthrough: true }) res: Response,
  ): Promise<StreamableFile> {
    const src = this.validateSource(source);
    const idx = parseInt(index || '0', 10) || 0;   // 👈 NEW

    const { stream, size, mimeType, filename } =
      await this.previousNcService.openEntryFile(src, ncId, entryId, idx);  // 👈 pass idx

    res.set({
      'Content-Type': mimeType,
      'Content-Length': size.toString(),
      'Content-Disposition': `inline; filename="${encodeURIComponent(filename)}"`,
      'Cache-Control': 'private, max-age=300',
    });
    return new StreamableFile(stream);
  }

  // DELETE /api/previous-nc/:source/:id/entries/:entryId/evidence
  @Delete(':source/:id/entries/:entryId/evidence')
  async deleteEntryEvidence(
    @Param('source') source: string,
    @Param('id', ParseIntPipe) ncId: number,
    @Param('entryId', ParseIntPipe) entryId: number,
    @Req() req: Request,
  ) {
    const src = this.validateSource(source);
    const userId = this.getCurrentUserId(req);

    this.logger.log(
      `DELETE /previous-nc/${src}/${ncId}/entries/${entryId}/evidence — user=${userId}`,
    );

    return this.previousNcService.deleteEntryEvidence(
      src,
      ncId,
      entryId,
      userId,
    );
  }

  // ═══════════════════════════════════════════════════════════════
  // 🆕 PHASE 2A — CLOSURE ENDPOINTS
  // (must come before :source/:id pattern routes)
  // ═══════════════════════════════════════════════════════════════

  // GET /api/previous-nc/:source/:id/closure
  // Fetch current closure state (rows, files, finalize status)
  @Get(':source/:id/closure')
  async getClosure(
    @Param('source') source: string,
    @Param('id', ParseIntPipe) ncId: number,
    @Req() req: Request,
  ) {
    const src = this.validateSource(source);
    const userId = this.getCurrentUserId(req);
    this.logger.log(`GET /previous-nc/${src}/${ncId}/closure — user=${userId}`);
    return this.closureService.getClosure(src, ncId);
  }

  // POST /api/previous-nc/:source/:id/closure
  // Save closure (multipart: signed_copy + signature + JSON fields)
  @Post(':source/:id/closure')
  @UseInterceptors(
    FileFieldsInterceptor(
      [
        { name: 'signed_copy', maxCount: 1 },
        { name: 'signature', maxCount: 1 },
      ],
      {
        storage: memoryStorage(),
        limits: { fileSize: 55 * 1024 * 1024 }, // 55 MB Multer-side guard (covers 50 MB max)
      },
    ),
  )
  async saveClosure(
    @Param('source') source: string,
    @Param('id', ParseIntPipe) ncId: number,
    @Body() body: any,
    @UploadedFiles()
    files: {
      signed_copy?: Express.Multer.File[];
      signature?: Express.Multer.File[];
    },
    @Req() req: Request,
  ) {
    const src = this.validateSource(source);
    const userId = this.getCurrentUserId(req);

    // Parse multipart JSON fields (they come as strings)
    let parsed;
    try {
      parsed = {
        closed_entry_ids: JSON.parse(body.closed_entry_ids || '[]'),
        rows: JSON.parse(body.rows || '[]'),
        verification_by_auditor: body.verification_by_auditor || '',
        auditor_name: body.auditor_name || '',
        closure_date: body.closure_date || '',
        send_to_client:
          body.send_to_client === 'true' || body.send_to_client === true,
        closure_to: body.closure_to || undefined,    // 🆕
        closure_cc: body.closure_cc || undefined,    // 🆕
        closure_bcc: body.closure_bcc || undefined,  // 🆕
        finalize: body.finalize === 'true' || body.finalize === true,
      };
    } catch (err: any) {
      throw new BadRequestException(
        `Invalid request body: ${err.message}`,
      );
    }

    this.logger.log(
      `POST /previous-nc/${src}/${ncId}/closure — user=${userId}, ` +
      `rows=${parsed.closed_entry_ids?.length || 0}, finalize=${parsed.finalize}, ` +
      `signed_copy=${!!files.signed_copy?.[0]}, signature=${!!files.signature?.[0]}`,
    );

    return this.closureService.saveClosure(
      src,
      ncId,
      parsed,
      {
        signedCopy: files.signed_copy?.[0],
        signature: files.signature?.[0],
      },
      userId,
    );
  }

  // GET /api/previous-nc/:source/:id/closure/file/:kind
  // Stream signed copy (PDF/DOC) or signature (PNG/JPG)
  @Get(':source/:id/closure/file/:kind')
  async getClosureFile(
    @Param('source') source: string,
    @Param('id', ParseIntPipe) ncId: number,
    @Param('kind') kind: string,
    @Res({ passthrough: true }) res: Response,
  ): Promise<StreamableFile> {
    const src = this.validateSource(source);
    if (kind !== 'signed_copy' && kind !== 'signature') {
      throw new BadRequestException(
        `Invalid file kind "${kind}" — must be signed_copy or signature`,
      );
    }

    const { stream, size, mimeType, filename } =
      await this.closureService.openClosureFile(src, ncId, kind as 'signed_copy' | 'signature');

    res.set({
      'Content-Type': mimeType,
      'Content-Length': size.toString(),
      'Content-Disposition': `inline; filename="${encodeURIComponent(filename)}"`,
      'Cache-Control': 'private, max-age=300',
    });

    return new StreamableFile(stream);
  }
  // previous-nc.controller.ts — add this route
  @Post('ai/draft')
  async aiDraft(@Body() body: { nc_type?: string; note: string }) {
    return this.previousNcService.draftFinding(body.nc_type || '', body.note);
  }
  // DELETE /api/previous-nc/:source/:id/closure
  // Discard a draft closure (refused if already finalized)
  @Delete(':source/:id/closure')
  async deleteClosure(
    @Param('source') source: string,
    @Param('id', ParseIntPipe) ncId: number,
    @Req() req: Request,
  ) {
    const src = this.validateSource(source);
    const userId = this.getCurrentUserId(req);
    this.logger.log(
      `DELETE /previous-nc/${src}/${ncId}/closure — user=${userId}`,
    );
    return this.closureService.deleteDraftClosure(src, ncId, userId);
  }

  // ═══════════════════════════════════════════════════════════════
  // EXISTING ENDPOINTS (continued — declared AFTER specific routes)
  // ═══════════════════════════════════════════════════════════════
  @Get('audit-assign-report')
  async auditAssignReport(@Req() req: Request) {
    const userId = this.getCurrentUserId(req);
    this.logger.log(`GET /previous-nc/audit-assign-report — user=${userId}`);
    return this.previousNcService.auditAssignReport();
  }

  // ═══════════════════════════════════════════════════════════════
  // 🆕 AUDIT → NC STATUS EXPORT + REPORT
  // (all literal "audit-nc-status..." routes MUST be declared BEFORE
  //  the greedy :source/:id route below)
  // ═══════════════════════════════════════════════════════════════

  // GET /api/previous-nc/audit-nc-status/export/excel?source=&status=&auditor=&year=&month=&search=
  @Get('audit-nc-status/export/excel')
  async exportNcStatusExcel(
    @Req() req: Request,
    @Query() q: any,
    @Res({ passthrough: true }) res: Response,
  ): Promise<StreamableFile> {
    const userId = this.getCurrentUserId(req);
    const data = await this.previousNcService.auditNcStatusReport(userId, q);
    const buf = await this.ncExcel.generate(
      { rows: data.rows, totals: data.totals },
      this.ncSubtitle(q),
    );
    const name = `audit-nc-status-${new Date().toISOString().slice(0, 10)}.xlsx`;
    res.set({
      'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      'Content-Disposition': `attachment; filename="${name}"`,
      'Content-Length': buf.length.toString(),
    });
    return new StreamableFile(buf);
  }

  // GET /api/previous-nc/audit-nc-status/export/pdf?source=&status=&auditor=&year=&month=&search=
  @Get('audit-nc-status/export/pdf')
  async exportNcStatusPdf(
    @Req() req: Request,
    @Query() q: any,
    @Res({ passthrough: true }) res: Response,
  ): Promise<StreamableFile> {
    const userId = this.getCurrentUserId(req);
    const data = await this.previousNcService.auditNcStatusReport(userId, q);
    const buf = await this.ncPdf.generate(
      { rows: data.rows, totals: data.totals },
      this.ncSubtitle(q),
    );
    const name = `audit-nc-status-${new Date().toISOString().slice(0, 10)}.pdf`;
    res.set({
      'Content-Type': 'application/pdf',
      'Content-Disposition': `attachment; filename="${name}"`,
      'Content-Length': buf.length.toString(),
    });
    return new StreamableFile(buf);
  }

  private ncSubtitle(q: any): string {
    const parts: string[] = [];
    if (q?.source && q.source !== 'All') parts.push(`Source: ${q.source}`);
    if (q?.status && q.status !== 'All') parts.push(`Status: ${q.status}`);
    if (q?.auditor) parts.push(`Auditor: ${q.auditor}`);
    if (q?.year) parts.push(`Year: ${q.year}`);
    if (q?.month) parts.push(`Month: ${q.month}`);
    if (q?.search) parts.push(`Search: "${q.search}"`);
    return parts.length ? parts.join('  |  ') : 'All conducted audits (no filter)';
  }

  // GET /api/previous-nc/audit-nc-status
  @Get('audit-nc-status')
  async auditNcStatus(@Query() q: any, @Req() req: Request) {
    const userId = this.getCurrentUserId(req);
    this.logger.log(`GET /previous-nc/audit-nc-status — user=${userId}`);
    return this.previousNcService.auditNcStatusReport(userId, {
      source: q.source,
      status: q.status,
      date_from: q.date_from,
      date_to: q.date_to,
      search: q.search,
    });
  }
  // GET /api/previous-nc/closures/list?source=&nc_type=&date_from=&date_to=&search=
  @Get('closures/list')
  async listClosures(@Query() q: any, @Req() req: Request) {
    this.getCurrentUserId(req);
    return this.closureService.listFinalizedClosures({
      source: q.source,
      nc_type: q.nc_type,
      date_from: q.date_from,
      date_to: q.date_to,
      search: q.search,
    });
  }
  @Delete('closures/:source/:id')
  async deleteFinalizedClosure(
    @Param('source') source: string,
    @Param('id', ParseIntPipe) id: number,
    @Req() req: Request,
  ) {
    const src = this.validateSource(source);
    const userId = this.getCurrentUserId(req);
    this.logger.log(
      `DELETE /previous-nc/closures/${src}/${id} — user=${userId}`,
    );
    return this.closureService.deleteFinalizedClosure(src, id, userId);
  }
  // GET /api/previous-nc/:source/:id
  // ⚠️ Greedy param route — MUST stay BELOW all literal-path routes above.
  @Get(':source/:id')
  async getOne(
    @Param('source') source: string,
    @Param('id') id: string,
    @Req() req: Request,
  ) {
    const validSource = this.validateSource(source);
    const validId = this.validateId(id);
    const userId = this.getCurrentUserId(req);

    this.logger.log(
      `GET /previous-nc/${validSource}/${validId} — user=${userId}`,
    );
    return this.previousNcService.findOne(validSource, validId, userId);
  }


  // DELETE /api/previous-nc/:source/:id
  @Delete(':source/:id')
  async deleteNc(
    @Param('source') source: string,
    @Param('id', ParseIntPipe) id: number,
    @Req() req: Request,
  ) {
    const src = this.validateSource(source);
    const userId = this.getCurrentUserId(req);
    this.logger.log(`DELETE /previous-nc/${src}/${id} — user=${userId}`);
    return this.previousNcService.deleteNc(src, id, userId);
  }
  // PATCH /api/previous-nc/:source/:id
  @Patch(':source/:id')
  async update(
    @Param('source') source: string,
    @Param('id', ParseIntPipe) id: number,
    @Body() dto: UpdatePreviousNcDto,
    @Req() req: Request,
  ) {
    const src = String(source || '').toUpperCase() as 'QRS' | 'TQS' | 'NEW';
    if (src !== 'QRS' && src !== 'TQS' && src !== 'NEW') {
      throw new BadRequestException('source must be QRS, TQS or NEW');
    }
    const currentUserId =
      (req as any).user?.sub ?? (req as any).user?.id ?? null;
    if (!currentUserId) {
      throw new UnauthorizedException('No user id on request');
    }
    return this.previousNcService.update(src, id, dto, Number(currentUserId));
  }

  // GET /api/previous-nc/:source/:id/pdf
  @Get(':source/:id/pdf')
  async getPdf(
    @Param('source') source: string,
    @Param('id') id: string,
    @Req() req: Request,
    @Res() res: Response,
  ) {
    const validSource = this.validateSource(source);
    const validId = this.validateId(id);
    const userId = this.getCurrentUserId(req);

    this.logger.log(
      `GET /previous-nc/${validSource}/${validId}/pdf — user=${userId}`,
    );

    const pdfBuffer = await this.pdfService.generateNcReportPdf(
      validSource,
      validId,
      userId,
    );

    res.setHeader('Content-Type', 'application/pdf');
    res.setHeader(
      'Content-Disposition',
      `inline; filename="NC_Report_${validSource}_${validId}.pdf"`,
    );
    res.setHeader('Content-Length', String(pdfBuffer.length));
    res.end(pdfBuffer);
  }

  // GET /api/previous-nc/:source/:id/attendance-pdf
  @Get(':source/:id/attendance-pdf')
  async getAttendancePdf(
    @Param('source') source: string,
    @Param('id') id: string,
    @Req() req: Request,
    @Res() res: Response,
  ) {
    const validSource = this.validateSource(source);
    const validId = this.validateId(id);
    const userId = this.getCurrentUserId(req);

    this.logger.log(
      `GET /previous-nc/${validSource}/${validId}/attendance-pdf — user=${userId}`,
    );

    const pdfBuffer = await this.pdfService.generateAttendancePdf(
      validSource,
      validId,
      userId,
    );

    res.setHeader('Content-Type', 'application/pdf');
    res.setHeader(
      'Content-Disposition',
      `inline; filename="NC_Attendance_${validSource}_${validId}.pdf"`,
    );
    res.setHeader('Content-Length', String(pdfBuffer.length));
    res.end(pdfBuffer);
  }

  // ═══════════════════════════════════════════════════════════════
  // VALIDATION HELPERS
  // ═══════════════════════════════════════════════════════════════
  private validateSource(source: string): 'QRS' | 'TQS' | 'NEW' {
    const upper = (source || '').toUpperCase();
    if (upper !== 'QRS' && upper !== 'TQS' && upper !== 'NEW') {
      throw new BadRequestException(
        `Invalid source "${source}" — must be QRS, TQS or NEW`,
      );
    }
    return upper as 'QRS' | 'TQS' | 'NEW';
  }

  private validateId(id: string): number {
    const n = Number(id);
    if (!Number.isFinite(n) || n <= 0) {
      throw new BadRequestException(`Invalid NC id "${id}"`);
    }
    return n;
  }


}