import { Injectable } from '@nestjs/common';
import PDFDocument from 'pdfkit';
import type { AgingReport, AgingRow } from './aging-analysis.service';

// ── Brand (QRS purple, matching the on-screen report) ──
const B = {
  name: 'QUALITY REGISTRAR SYSTEMS — QRS & TQS',
  purple: '#8B14D4',
  purpleDark: '#4A0080',
  purpleLight: '#EDE4FB',
  white: '#FFFFFF',
  gray50: '#F8F9FB',
  gray200: '#E5E7EB',
  gray500: '#6B7280',
  gray700: '#374151',
  gray900: '#111827',
  blue: '#2563EB',
  green: '#15803D',
  orange: '#C2410C',
  amber: '#B45309',
  red: '#B91C1C',
  cyan: '#0891B2',
};

const M = 40;

function fmtDate(d?: string | null): string {
  if (!d) return '—';
  const dt = new Date(d);
  if (isNaN(dt.getTime())) return '—';
  return dt.toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' });
}

@Injectable()
export class AgingReportPdfService {
  async build(report: AgingReport): Promise<Buffer> {
    const doc: any = new PDFDocument({
      layout: 'landscape',
      size: 'A4',
      margins: { top: M, bottom: 50, left: M, right: M },
      bufferPages: true,
      info: { Title: 'Aging Analysis Report', Author: 'Quality Registrar Systems' },
    });

    const buffers: Buffer[] = [];
    doc.on('data', (c: Buffer) => buffers.push(c));
    const done = new Promise<Buffer>((resolve, reject) => {
      doc.on('end', () => resolve(Buffer.concat(buffers)));
      doc.on('error', reject);
    });

    const title = report.meta.category
      ? `Aging Analysis — ${report.meta.category} — ${report.meta.period_label}`
      : `Aging Analysis — ${report.meta.period_label}`;
    const subtitle = `Source: ${report.meta.source_filter}`;

    this.addHeader(doc, title, subtitle);
    this.addStatCards(doc, report);
    this.addSectionTitle(doc, 'Certificate detail — by original registration');
    this.drawTable(doc, report);
    this.addFooters(doc);

    doc.end();
    return done;
  }

  // ── Header bar ──
  private addHeader(doc: any, title: string, subtitle: string) {
    const pw = doc.page.width;
    const cw = pw - M * 2;

    doc.rect(0, 0, pw, 60).fill(B.purpleDark);
    doc.font('Helvetica-Bold').fontSize(14).fillColor(B.white).text(B.name, M, 14, { width: cw / 2 });
    doc.font('Helvetica').fontSize(9).fillColor(B.white).text(title, M, 34, { width: cw / 2 });

    doc.font('Helvetica').fontSize(8).fillColor(B.white)
      .text(`Generated: ${fmtDate(new Date().toISOString())}`, pw / 2, 14, { width: cw / 2, align: 'right' });
    doc.text(subtitle, pw / 2, 28, { width: cw / 2, align: 'right' });

    doc.rect(0, 60, pw, 3).fill(B.purple);
    doc.fillColor(B.gray900);
    doc.y = 75;
  }

  // ── Stat cards (incl. Expired) ──
  private addStatCards(doc: any, report: AgingReport) {
    const s = report.summary;
    const cats = Object.entries(s.byCategory);

    const stats: { label: string; value: string; color: string }[] = [
      { label: 'Total Due', value: String(s.total), color: B.purpleDark },
      ...cats.map(([k, v]) => ({
        label: k,
        value: String(v),
        color: k.includes('1st') ? B.cyan : k.includes('2nd') ? B.cyan : B.purple,
      })),
      { label: 'Expired', value: String(s.expired), color: B.red },
      { label: 'Expiring <=90d', value: String(s.expiringWithin90), color: B.amber },
    ];

    const cw = doc.page.width - M * 2;
    const gap = 8;
    const cardW = (cw - gap * (stats.length - 1)) / stats.length;
    const sy = doc.y;
    const cardH = 42;

    stats.forEach((st, i) => {
      const x = M + i * (cardW + gap);
      doc.roundedRect(x, sy, cardW, cardH, 4).fill(B.gray50);
      doc.rect(x, sy + 4, 3, cardH - 8).fill(st.color);
      doc.font('Helvetica-Bold').fontSize(6.5).fillColor(B.gray500)
        .text(st.label.toUpperCase(), x + 9, sy + 8, { width: cardW - 14, ellipsis: true, lineBreak: false });
      doc.font('Helvetica-Bold').fontSize(15).fillColor(st.color)
        .text(st.value, x + 9, sy + 19, { width: cardW - 14 });
    });

    doc.fillColor(B.gray900);
    doc.y = sy + cardH + 14;
  }

  private addSectionTitle(doc: any, title: string) {
    doc.font('Helvetica-Bold').fontSize(11).fillColor(B.gray900).text(title, M, doc.y);
    doc.y += 6;
  }

  // ── Table ──
  private drawTable(doc: any, report: AgingReport) {
    const headers: { label: string; width: number; align?: string }[] = [
      { label: 'S.NO', width: 32, align: 'center' },
      { label: 'CERT NO', width: 82 },
      { label: 'COMPANY', width: 188 },
      { label: 'STANDARD', width: 62 },
      { label: 'CATEGORY', width: 92 },
      { label: 'ORIG. REG', width: 66, align: 'center' },
      { label: 'ISSUE', width: 62, align: 'center' },
      { label: 'EXPIRE', width: 62, align: 'center' },
      { label: 'AGING BUCKET', width: 76, align: 'center' },
      { label: 'DAYS TO EXP', width: 52, align: 'right' },
      { label: 'STATUS', width: 56, align: 'center' },
    ];

    const cw = doc.page.width - M * 2;
    const totalW = headers.reduce((s, h) => s + h.width, 0);
    const scale = cw / totalW;
    const rowH = 16;
    const headerH = 22;
    let y = doc.y;

    const bottom = () => doc.page.height - 60;

    const drawHeaderRow = (atY: number): number => {
      doc.rect(M, atY, cw, headerH).fill(B.purple);
      let x = M;
      headers.forEach((h) => {
        const colW = h.width * scale;
        doc.font('Helvetica-Bold').fontSize(7).fillColor(B.white)
          .text(h.label, x + 4, atY + 6, { width: colW - 8, align: (h.align as any) || 'left', ellipsis: true, lineBreak: false });
        x += colW;
      });
      return atY + headerH;
    };

    if (y + headerH + rowH * 3 > bottom()) { doc.addPage(); y = M; }
    y = drawHeaderRow(y);

    const bucketColor = (bucket: string): string => {
      if (bucket === 'Expired') return B.red;
      if (bucket === '0-30 days') return B.orange;
      if (bucket === '31-90 days') return B.amber;
      if (bucket === '180+ days') return B.green;
      return B.gray700;
    };
    const catColor = (c: string): string =>
      c.includes('Re-cert') ? B.purple : c.includes('1st') || c.includes('2nd') ? B.cyan : B.gray700;
    const statusText = (row: AgingRow): string =>
      row.source === 'Manual' ? (row.status ?? '—') : row.source;

    report.rows.forEach((row, idx) => {
      if (y + rowH > bottom()) { doc.addPage(); y = drawHeaderRow(M); }
      if (idx % 2 === 1) doc.rect(M, y, cw, rowH).fill(B.gray50);
      doc.moveTo(M, y + rowH).lineTo(M + cw, y + rowH).strokeColor(B.gray200).lineWidth(0.3).stroke();

      const dte = row.days_to_expiry;
      const cells: { value: string; color: string; bold?: boolean; align?: string }[] = [
        { value: String(idx + 1), color: B.gray500, align: 'center' },
        { value: row.certificate_no || '—', color: B.gray900, bold: true },
        { value: row.company_name || '—', color: B.gray900 },
        { value: row.standard || '—', color: B.gray700 },
        { value: row.category || '—', color: catColor(row.category), bold: true, align: 'left' },
        { value: fmtDate(row.originally_registered), color: B.gray700, align: 'center' },
        { value: fmtDate(row.issue_date), color: B.gray700, align: 'center' },
        { value: fmtDate(row.expire_date), color: B.gray700, align: 'center' },
        { value: row.aging_bucket, color: bucketColor(row.aging_bucket), bold: true, align: 'center' },
        {
          value: dte == null ? '—' : String(dte),
          color: dte == null ? B.gray500 : dte < 0 ? B.red : dte <= 90 ? B.orange : B.green,
          bold: true,
          align: 'right',
        },
        { value: statusText(row), color: B.gray700, align: 'center' },
      ];

      let x = M;
      cells.forEach((cell, ci) => {
        const colW = headers[ci].width * scale;
        doc.font(cell.bold ? 'Helvetica-Bold' : 'Helvetica').fontSize(7).fillColor(cell.color)
          .text(cell.value, x + 4, y + 5, {
            width: colW - 8,
            align: (cell.align as any) || (headers[ci].align as any) || 'left',
            ellipsis: true,
            lineBreak: false,
          });
        x += colW;
      });
      y += rowH;
    });

    // Total row
    doc.moveTo(M, y).lineTo(M + cw, y).strokeColor(B.purple).lineWidth(1.5).stroke();
    doc.rect(M, y, cw, rowH + 4).fill(B.purpleLight);
    const totalCells: (string | null)[] = [
      'TOTAL',
      `${report.summary.total} certs`,
      null, null, null, null, null, null,
      `${report.summary.expired} expired`,
      null, null,
    ];
    let tx = M;
    totalCells.forEach((val, ci) => {
      const colW = headers[ci].width * scale;
      if (val !== null) {
        doc.font('Helvetica-Bold').fontSize(8).fillColor(ci === 8 ? B.red : B.purpleDark)
          .text(val, tx + 4, y + 6, { width: colW - 8, align: (headers[ci].align as any) || 'left', ellipsis: true, lineBreak: false });
      }
      tx += colW;
    });
    doc.moveTo(M, y + rowH + 4).lineTo(M + cw, y + rowH + 4).strokeColor(B.purple).lineWidth(1.5).stroke();

    doc.y = y + rowH + 16;
    doc.fillColor(B.gray900);
  }

  // ── Footers with page numbers ──
  private addFooters(doc: any) {
    const range = doc.bufferedPageRange();
    for (let i = 0; i < range.count; i++) {
      doc.switchToPage(range.start + i);
      const pH = doc.page.height;
      const pW = doc.page.width;
      const cw3 = (pW - M * 2) / 3;

      doc.moveTo(M, pH - 35).lineTo(pW - M, pH - 35).strokeColor(B.gray200).lineWidth(0.5).stroke();
      doc.font('Helvetica').fontSize(7).fillColor(B.gray500);
      doc.text(B.name, M, pH - 28, { width: cw3, align: 'left' });
      doc.text(`Page ${i + 1} of ${range.count}`, M + cw3, pH - 28, { width: cw3, align: 'center' });
      doc.text(`Printed: ${fmtDate(new Date().toISOString())}`, M + cw3 * 2, pH - 28, { width: cw3, align: 'right' });
    }
  }
}