import { Injectable, Logger } from '@nestjs/common';
import PDFDocument = require('pdfkit');
import { promises as fs } from 'fs';
import * as https from 'https';

import { AuditChecklist } from '../entities/audit-checklist.entity';

// ═══════════════════════════════════════════════════════════════════
//  Server-side checklist PDF — styled EXACTLY like the QRS NC
//  (Corrective & Preventive Action Request) template: logo header
//  table, #adcff6 label cells, black borders, Arial/Helvetica,
//  "Page X of Y", signature table, green footer note.
//
//  Logo: set CHECKLIST_LOGO_PATH to a local file (fastest), or it is
//  fetched once from CHECKLIST_LOGO_URL
//  (default https://crm.qrs.ae/qrslogo.jpg) and cached in memory.
// ═══════════════════════════════════════════════════════════════════

const A4 = { w: 595.28, h: 841.89 };
const M = 34;
const CW = A4.w - M * 2;

const TH_BG = '#adcff6';
const INK = '#0a0a0a';
const TH_INK = '#333333';
const GREEN_BG = '#d4edda';
const GREEN_INK = '#155724';

const LOGO_URL = process.env.CHECKLIST_LOGO_URL || 'https://crm.qrs.ae/qrslogo.jpg';
const LOGO_PATH = process.env.CHECKLIST_LOGO_PATH || '';

export interface ChecklistPdfData {
  auditCode: string;
  companyName: string;
  auditLine: string;
  standardName: string;
  items: string[];
  dueDate?: string | null;
  leadAuditorName?: string | null;
  generatedAt?: string | null;
}

@Injectable()
export class ChecklistPdfService {
  private readonly logger = new Logger(ChecklistPdfService.name);
  private logoCache: Buffer | null | undefined; // undefined = not tried yet

  dataFromChecklist(checklist: AuditChecklist): ChecklistPdfData {
    const row: any = checklist.audit_schedule_row || {};
    const lead: any = row.lead_auditor || null;
    const auditLine = [
      row.audit_type,
      row.audit_stage,
      row.accreditation,
      row.schedule?.schedule_date,
      row.audit_time_label,
    ]
      .filter(Boolean)
      .join(' · ');
    const leadName = lead ? [lead.firstName, lead.lastName].filter(Boolean).join(' ') : '';

    // Show all standards from the audit row (multi-standard support).
    // Falls back to the single checklist's standard if row.standards is empty.
    const rowStandards: any[] = row.standards || [];
    const standardName =
      rowStandards.length > 1
        ? rowStandards.map((s: any) => s.name).join(', ')
        : (checklist.standard as any)?.name || '';

    return {
      auditCode: row.audit_code || `audit ${checklist.audit_schedule_row_id}`,
      companyName: (checklist.company as any)?.name || '',
      auditLine,
      standardName,
      items: (checklist.items || []).map((i) => (i.template_item as any)?.item_text || ''),
      dueDate: checklist.due_date || null,
      leadAuditorName: leadName || null,
      generatedAt: new Date().toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' }),
    };
  }

  filenameFor(checklist: AuditChecklist): string {
    const row: any = checklist.audit_schedule_row || {};
    const code = (row.audit_code || `audit-${checklist.audit_schedule_row_id}`).replace(/[^\w.-]+/g, '-');
    const std = ((checklist.standard as any)?.name || `standard-${checklist.standard_id}`)
      .replace(/[^\w.-]+/g, '-')
      .slice(0, 40);
    return `checklist-${code}-${std}.pdf`;
  }

  /** Load the QRS logo once: local path first, then HTTP, then null. */
  private async getLogo(): Promise<Buffer | null> {
    if (this.logoCache !== undefined) return this.logoCache;
    try {
      if (LOGO_PATH) {
        this.logoCache = await fs.readFile(LOGO_PATH);
        return this.logoCache;
      }
    } catch (err: any) {
      this.logger.warn(`CHECKLIST_LOGO_PATH unreadable (${err.message}) - trying URL`);
    }
    try {
      const f: any = (globalThis as any).fetch;
      if (typeof f === 'function') {
        const res = await f(LOGO_URL);
        if (res.ok) {
          this.logoCache = Buffer.from(await res.arrayBuffer());
          return this.logoCache;
        }
        throw new Error(`HTTP ${res.status}`);
      }
      this.logoCache = await new Promise<Buffer>((resolve, reject) => {
        https
          .get(LOGO_URL, (res) => {
            if ((res.statusCode || 0) >= 400) return reject(new Error(`HTTP ${res.statusCode}`));
            const parts: Buffer[] = [];
            res.on('data', (d) => parts.push(d));
            res.on('end', () => resolve(Buffer.concat(parts)));
          })
          .on('error', reject);
      });
      return this.logoCache;
    } catch (err: any) {
      this.logger.warn(`Checklist logo unavailable (${err.message}) - PDF renders with wordmark only.`);
      this.logoCache = null;
      return null;
    }
  }

  /** Render the checklist as an A4 PDF buffer (NC-template style, multi-page). */
  async generate(data: ChecklistPdfData): Promise<Buffer> {
    const logoBuffer = await this.getLogo();
    return new Promise((resolve, reject) => {
      const doc = new PDFDocument({
        size: 'A4',
        margin: 0,
        bufferPages: true,
        info: { Title: `Document Request Checklist — ${data.auditCode}` },
      });
      const chunks: Buffer[] = [];
      doc.on('data', (c: Buffer) => chunks.push(c));
      doc.on('end', () => resolve(Buffer.concat(chunks)));
      doc.on('error', reject);

    let y = M;
    const pageMarks: { x: number; y: number; w: number }[] = []; // header "Page X of Y" cell coords per page

    // ── header (logo table + title row), repeated on every page ──
    const drawHeader = () => {
      y = M;
      const leftW = Math.round(CW * 0.87);
      const rightW = CW - leftW;
      const r1 = 58, r2 = 30;

      doc.lineWidth(0.9).strokeColor("#000");
      // row 1 cells
      doc.rect(M, y, leftW, r1).stroke();
      doc.rect(M + leftW, y, rightW, r1).stroke();

      // centered logo + wordmark in left cell
      const line1 = "QUALITY REGISTRAR";
      const line2 = "SYSTEMS";
      doc.font("Helvetica-Bold").fontSize(15);
      const t1 = doc.widthOfString(line1, { characterSpacing: 1 });
      const logoW = logoBuffer ? 44 : 0;
      const gap = logoBuffer ? 12 : 0;
      const total = logoW + gap + t1;
      let sx = M + (leftW - total) / 2;
      if (logoBuffer) {
        try { doc.image(logoBuffer, sx, y + 8, { height: 42 }); } catch {}
        sx += logoW + gap;
      }
      doc.fillColor("#000").text(line1, sx, y + 12, { characterSpacing: 1, lineBreak: false });
      const t2 = doc.widthOfString(line2, { characterSpacing: 1 });
      doc.text(line2, sx + (t1 - t2) / 2, y + 31, { characterSpacing: 1, lineBreak: false });

      // right cell: Page X of Y (filled on pass 2)
      pageMarks.push({ x: M + leftW + 4, y: y + 7, w: rightW - 10 });

      // row 2
      const y2 = y + r1;
      doc.rect(M, y2, leftW, r2).stroke();
      doc.rect(M + leftW, y2, rightW, r2).stroke();
      doc.font("Helvetica-Bold").fontSize(12.5).fillColor("#000")
        .text("DOCUMENT REQUEST CHECKLIST", M, y2 + 9, { width: leftW, align: "center", characterSpacing: 0.4 });
      doc.font("Helvetica").fontSize(8.5).fillColor(TH_INK)
        .text("Form", M + leftW, y2 + r2 - 14, { width: rightW - 6, align: "right" });

      y = y2 + r2 + 12;
    };

    drawHeader();

    // ── info table (blue label cells, 20% width) ──
    const LABEL_W = Math.round(CW * 0.2);
    const info: [string, string, boolean][] = [
      ['COMPANY', data.companyName || '—', true],
      ['AUDIT', data.auditLine || '—', false],
      ['STANDARD', data.standardName || '—', false],
      ['DOCUMENTS', `${data.items.length} requested${data.dueDate ? `   ·   Due: ${data.dueDate}` : ''}`, false],
    ];
    doc.lineWidth(0.8);
    for (const [label, value, bold] of info) {
      const vh = doc.font(bold ? "Helvetica-Bold" : "Helvetica").fontSize(10)
        .heightOfString(String(value), { width: CW - LABEL_W - 16 });
      const rh = Math.max(24, vh + 12);
      doc.rect(M, y, LABEL_W, rh).fillAndStroke(TH_BG, "#000");
      doc.rect(M + LABEL_W, y, CW - LABEL_W, rh).stroke("#000");
      doc.font("Helvetica-Bold").fontSize(9.5).fillColor(TH_INK)
        .text(label, M + 8, y + (rh - 9) / 2, { lineBreak: false });
      doc.font(bold ? "Helvetica-Bold" : "Helvetica").fontSize(10).fillColor(INK)
        .text(String(value), M + LABEL_W + 8, y + (rh - vh) / 2, { width: CW - LABEL_W - 16 });
      y += rh;
    }
    y += 12;

    // ── items table ──
    const C_NUM = 32, C_PROV = 64, C_REM = 96;
    const C_DOC = CW - C_NUM - C_PROV - C_REM;
    const X_NUM = M, X_DOC = M + C_NUM, X_PROV = X_DOC + C_DOC, X_REM = X_PROV + C_PROV;

    const itemsHead = () => {
      const hh = 22;
      doc.rect(X_NUM, y, C_NUM, hh).fillAndStroke(TH_BG, "#000");
      doc.rect(X_DOC, y, C_DOC, hh).fillAndStroke(TH_BG, "#000");
      doc.rect(X_PROV, y, C_PROV, hh).fillAndStroke(TH_BG, "#000");
      doc.rect(X_REM, y, C_REM, hh).fillAndStroke(TH_BG, "#000");
      doc.font("Helvetica-Bold").fontSize(8).fillColor(TH_INK);
      doc.text("S#", X_NUM + 6, y + 7.5, { lineBreak: false });
      doc.text("DOCUMENT REQUIRED", X_DOC + 8, y + 7.5, { lineBreak: false });
      doc.text("PROVIDED", X_PROV, y + 7.5, { width: C_PROV, align: "center" });
      doc.text("REMARKS", X_REM, y + 7.5, { width: C_REM, align: "center" });
      y += hh;
    };

    itemsHead();
    const FOOT_RESERVE = 158; // signature + footer strip on the last page
    data.items.forEach((text: string, i: number) => {
      const th = doc.font("Helvetica").fontSize(10).heightOfString(text || "—", { width: C_DOC - 16 });
      const rh = Math.max(24, th + 12);
      if (y + rh > A4.h - M - 26) {
        doc.addPage();
        drawHeader();
        itemsHead();
      }
      doc.lineWidth(0.8).strokeColor("#000");
      doc.rect(X_NUM, y, C_NUM, rh).stroke();
      doc.rect(X_DOC, y, C_DOC, rh).stroke();
      doc.rect(X_PROV, y, C_PROV, rh).stroke();
      doc.rect(X_REM, y, C_REM, rh).stroke();
      doc.font("Helvetica").fontSize(10).fillColor(INK);
      doc.text(`${i + 1}.`, X_NUM + 6, y + (rh - 10) / 2, { lineBreak: false });
      doc.text(text || "—", X_DOC + 8, y + (rh - th) / 2, { width: C_DOC - 16 });
      const bs = 9;
      doc.lineWidth(1).rect(X_PROV + (C_PROV - bs) / 2, y + (rh - bs) / 2, bs, bs).stroke("#333");
      y += rh;
    });

    // ── signature table + footer note (last page) ──
    if (y > A4.h - M - FOOT_RESERVE) {
      doc.addPage();
      drawHeader();
    }
    y += 16;
    const q = CW / 4;
    const sig = [
      ["Auditor Signature:", data.leadAuditorName || "", "Auditee Signature:", ""],
      ["Date:", "", "Date:", ""],
    ];
    doc.lineWidth(0.8);
    for (const row of sig) {
      const rh = 26;
      for (let c = 0; c < 4; c++) {
        doc.rect(M + q * c, y, q, rh).stroke("#000");
        doc.font(c % 2 === 0 ? "Helvetica-Bold" : "Helvetica").fontSize(9.5)
          .fillColor(c % 2 === 0 ? TH_INK : INK)
          .text(row[c], M + q * c + 7, y + 8, { width: q - 14, lineBreak: false });
      }
      y += rh;
    }

    // green footer note
    y += 14;
    const note = `Please upload each requested document on your QRS client portal${data.dueDate ? ` before ${data.dueDate}` : ""}. This checklist PDF is attached to your notification email for reference.`;
    const nh = doc.font("Helvetica").fontSize(10).heightOfString(note, { width: CW - 24 }) + 18;
    doc.roundedRect(M, y, CW, nh, 4).fill(GREEN_BG);
    doc.fillColor(GREEN_INK).text(note, M + 12, y + 9, { width: CW - 24 });

    // ── pass 2: page numbers (header cell + bottom-right) ──
    const range = doc.bufferedPageRange();
    for (let i = 0; i < range.count; i++) {
      doc.switchToPage(i);
      const mk = pageMarks[i];
      doc.font("Helvetica").fontSize(8.5).fillColor(TH_INK);
      if (mk) doc.text(`Page ${i + 1} of ${range.count}`, mk.x, mk.y, { width: mk.w, align: "right" });
      doc.fontSize(8).text(`Page ${i + 1} of ${range.count}`, M, A4.h - M + 6, { width: CW, align: "right", lineBreak: false });
    }


      doc.end();
    });
  }
}