import { Injectable, Logger } from '@nestjs/common';
import { PDFDocument } from 'pdf-lib';
import * as puppeteer from 'puppeteer';
import * as crypto from 'crypto';
import { promises as fs } from 'fs';
import { existsSync } from 'fs';
import * as path from 'path';
import {
  renderVerificationHtml,
  ClosureTemplateFinding,
} from '../templates/closure-verification.html';
import { NcFileStorageService } from './nc-file-storage.service';

export interface ClosurePdfInput {
  source: 'QRS' | 'TQS' | 'NEW';
  ncId: number;
  ncCode: string;
  companyName: string;
  auditorName: string;
  closureDate: string;
  verificationText: string;
  findings: ClosureTemplateFinding[];
  signedCopyAbsPath: string | null;
  signatureAbsPath: string | null;
}

export interface ClosurePdfResult {
  dbPath: string;
  /** Hash of the verification page content (data only — stable + reproducible) */
  contentHash: string;
  /** Hash of the final merged file bytes (canonical, in PDF metadata + UI) */
  fileHash: string;
  size: number;
  pages: number;
}

@Injectable()
export class ClosurePdfService {
  private readonly logger = new Logger(ClosurePdfService.name);

  constructor(private readonly fileStorage: NcFileStorageService) { }

  async generateMergedPdf(input: ClosurePdfInput): Promise<ClosurePdfResult> {
    this.logger.log(
      `[CLOSURE-PDF] Generating merged PDF for ${input.source}/NC${input.ncId} ` +
      `(findings=${input.findings.length}, signedCopy=${!!input.signedCopyAbsPath}, ` +
      `signature=${!!input.signatureAbsPath})`,
    );

    // ─── 1. Pre-count signed copy pages so we know total ───
    let signedCopyDoc: PDFDocument | null = null;
    let signedPageCount = 0;
    if (input.signedCopyAbsPath && existsSync(input.signedCopyAbsPath)) {
      const ext = path
        .extname(input.signedCopyAbsPath)
        .slice(1)
        .toLowerCase();
      if (ext === 'pdf') {
        try {
          const bytes = await fs.readFile(input.signedCopyAbsPath);
          signedCopyDoc = await PDFDocument.load(bytes, {
            ignoreEncryption: true,
          });

          // 🆕 Prevent duplicate verification pages on re-finalize.
          // If the "signed copy" is actually a PREVIOUSLY-MERGED closure
          // (our own output), drop its trailing verification page so we
          // rebuild from the ORIGINAL signed copy instead of stacking.
          const producer = String(signedCopyDoc.getProducer() || '');
          const keywords = String(signedCopyDoc.getKeywords() || '');
          const isOwnMergedClosure =
            producer.includes('Scheme Certification') ||
            keywords.includes('nc:');
          if (isOwnMergedClosure && signedCopyDoc.getPageCount() > 1) {
            signedCopyDoc.removePage(signedCopyDoc.getPageCount() - 1);
            this.logger.log(
              `[CLOSURE-PDF] Re-finalize detected — stripped stale verification page ` +
              `(now ${signedCopyDoc.getPageCount()} signed-copy page(s))`,
            );
          }

          signedPageCount = signedCopyDoc.getPageCount();
        } catch (err: any) {
          this.logger.warn(
            `[CLOSURE-PDF] Could not load signed copy: ${err.message}. Verification page only.`,
          );
          signedCopyDoc = null;
        }
      } else {
        this.logger.log(
          `[CLOSURE-PDF] Signed copy is .${ext} (not PDF) — verification page only`,
        );
      }
    }

    const totalPages = signedPageCount + 1; // +1 for our verification page
    const verificationPageNumber = totalPages; // it's the LAST page

    // ─── 2. Render verification HTML with correct page numbers ───
    const { html, contentHash } = renderVerificationHtml({
      source: input.source,
      ncId: input.ncId,
      ncCode: input.ncCode,
      companyName: input.companyName,
      auditorName: input.auditorName,
      closureDate: input.closureDate,
      verificationText: input.verificationText,
      findings: input.findings,
      signatureAbsPath: input.signatureAbsPath,
      currentPage: verificationPageNumber,
      totalPages: totalPages,
    });

    // ─── 3. Convert HTML to PDF (LANDSCAPE) ───
    const verificationPdfBytes = await this.htmlToPdf(html);

    // ─── 4. Merge: signed copy FIRST, verification page LAST ───
    let mergedDoc: PDFDocument;

    if (signedCopyDoc && signedPageCount > 0) {
      // Start with signed copy
      mergedDoc = await PDFDocument.load(await signedCopyDoc.save());

      // Append verification page
      const verificationDoc = await PDFDocument.load(verificationPdfBytes);
      const verifPages = await mergedDoc.copyPages(
        verificationDoc,
        verificationDoc.getPageIndices(),
      );
      for (const page of verifPages) {
        mergedDoc.addPage(page);
      }
      this.logger.log(
        `[CLOSURE-PDF] Merged ${signedPageCount} signed copy page(s) + 1 verification page`,
      );
    } else {
      // No signed copy — just the verification page
      mergedDoc = await PDFDocument.load(verificationPdfBytes);
      this.logger.log(`[CLOSURE-PDF] No signed copy — verification page only`);
    }

    // ─── 5. Set PDF metadata ───
    mergedDoc.setTitle(`Final Closure — ${input.ncCode}`);
    mergedDoc.setAuthor(input.auditorName || 'Auditor');
    mergedDoc.setSubject('NC Final Closure & Verification');
    mergedDoc.setCreator('NC Closure System');
    mergedDoc.setProducer('Scheme Certification — Phase 2B');
    mergedDoc.setCreationDate(new Date());
    mergedDoc.setModificationDate(new Date());
    mergedDoc.setKeywords([
      `nc:${input.ncCode}`,
      `content-sha256:${contentHash}`,
    ]);

    // ─── 6. Serialize ───
    const finalBytes = await mergedDoc.save();
    const finalBuffer = Buffer.from(finalBytes);

    // ─── 7. Hash the final file ───
    const fileHash = crypto
      .createHash('sha256')
      .update(finalBuffer)
      .digest('hex');

    // ─── 8. Save to disk ───
    const saved = await this.fileStorage.saveMergedClosurePdf(
      input.source,
      input.ncId,
      finalBuffer,
      input.ncCode,
    );

    this.logger.log(
      `[CLOSURE-PDF] Merged PDF saved: ${saved.dbPath} ` +
      `(size=${saved.size}, pages=${mergedDoc.getPageCount()}, ` +
      `content-sha=${contentHash.substring(0, 12)}…, ` +
      `file-sha=${fileHash.substring(0, 12)}…)`,
    );

    return {
      dbPath: saved.dbPath,
      contentHash,
      fileHash,
      size: saved.size,
      pages: mergedDoc.getPageCount(),
    };
  }

  // ═══════════════════════════════════════════════════════════════════
  // HTML → PDF via Puppeteer (LANDSCAPE)
  // ═══════════════════════════════════════════════════════════════════

  private async htmlToPdf(html: string): Promise<Buffer> {
    const launchOptions: any = {
      headless: true,
      args: [
        '--no-sandbox',
        '--disable-setuid-sandbox',
        '--disable-dev-shm-usage',
        '--disable-gpu',
        '--font-render-hinting=none',
      ],
    };

    if (process.env.PUPPETEER_EXECUTABLE_PATH) {
      launchOptions.executablePath = process.env.PUPPETEER_EXECUTABLE_PATH;
    }

    const browser = await puppeteer.launch(launchOptions);
    try {
      const page = await browser.newPage();
      await page.setViewport({
        width: 1600,
        height: 1130,
        deviceScaleFactor: 2, // 🆕 2× raster — sharp seal + crisp text
      });
      await page.setContent(html, {
        waitUntil: 'domcontentloaded',

        timeout: 30000,
      });

      // 🆕 Ensure the seal's web fonts are fully loaded before printing
      await page.evaluateHandle('document.fonts.ready');
      const pdf = await page.pdf({
        format: 'A4',
        landscape: true, // 🆕 PHASE 2B v2 — landscape orientation
        printBackground: true,
        margin: { top: '0', right: '0', bottom: '0', left: '0' },
        preferCSSPageSize: true,
      });
      return Buffer.from(pdf);
    } finally {
      await browser.close().catch(() => null);
    }
  }
}