import { Request, Response } from 'express';
import PDFDocument from 'pdfkit';
import { asyncHandler } from '../../utils/asyncHandler';
import { LoggingService } from './LoggingService';
import { LogLevel } from '../../models/Logging';
import { existsSync, readFileSync } from 'fs';
import { resolve } from 'path';

interface AuthenticatedRequest extends Request {
  user?: {
    userId: string;
    email: string;
    role: string;
  };
}

/**
 * Logging PDF Controller
 * Handles professional PDF report generation for logging data
 */
export class LoggingPDFController {
  // Brand colors
  private static readonly BRAND_BLUE = '#1E3A8A';
  private static readonly BRAND_LIGHT_BLUE = '#3B82F6';
  private static readonly BRAND_BG_BLUE = '#EFF6FF';
  private static readonly ERROR_RED = '#DC2626';
  private static readonly CRITICAL_DARK_RED = '#991B1B';
  private static readonly WARN_ORANGE = '#F59E0B';
  private static readonly INFO_BLUE = '#2563EB';
  private static readonly DEBUG_GRAY = '#6B7280';
  private static readonly ROW_ALT_COLOR = '#F8FAFC';

  // Typography
  private static readonly FONT_TITLE = 24;
  private static readonly FONT_SECTION = 16;
  private static readonly FONT_SUBSECTION = 12;
  private static readonly FONT_BODY = 10;
  private static readonly FONT_TABLE = 8;
  private static readonly FONT_SMALL = 7;

  // Layout
  private static readonly MARGIN_TOP = 60;
  private static readonly MARGIN_BOTTOM = 60;
  private static readonly MARGIN_LEFT = 50;
  private static readonly MARGIN_RIGHT = 50;
  private static readonly HEADER_HEIGHT = 80;
  private static readonly FOOTER_HEIGHT = 40;
  private static readonly LOGO_SIZE = 40;

  /**
   * Load logo image with fallback
   */
  private static loadLogo(): Buffer | null {
    try {
      // Try multiple possible paths (development and production)
      const paths = [
        resolve(__dirname, '../../../uploads/quick-connect.ico'),
        resolve(__dirname, '../../uploads/quick-connect.ico'),
        resolve(process.cwd(), 'uploads/quick-connect.ico'),
      ];

      for (const logoPath of paths) {
        if (existsSync(logoPath)) {
          return readFileSync(logoPath);
        }
      }
    } catch (error) {
      console.warn('Logo file not found, using text-only branding');
    }
    return null;
  }

  /**
   * Draw page header with logo and branding
   */
  private static drawHeader(doc: any, pageNumber: number, totalPages: number, isFirstPage: boolean = false) {
    const logo = this.loadLogo();
    const currentY = doc.y;

    // Blue header background
    doc.rect(0, 0, doc.page.width, this.HEADER_HEIGHT)
      .fill(this.BRAND_BLUE);

    // Logo
    let logoX = this.MARGIN_LEFT;
    if (logo) {
      try {
        doc.image(logo, logoX, 20, {
          width: this.LOGO_SIZE,
          height: this.LOGO_SIZE,
          fit: [this.LOGO_SIZE, this.LOGO_SIZE],
        });
        logoX += this.LOGO_SIZE + 15;
      } catch (error) {
        // Fallback to text if image fails
        logoX = this.MARGIN_LEFT;
      }
    }

    // Title
    doc.fillColor('#FFFFFF')
      .fontSize(this.FONT_TITLE)
      .font('Helvetica-Bold')
      .text('Quick Connect', logoX, 25, {
        width: doc.page.width - logoX - this.MARGIN_RIGHT,
      })
      .fontSize(this.FONT_SUBSECTION)
      .font('Helvetica')
      .text('System Logging Report', logoX, 50, {
        width: doc.page.width - logoX - this.MARGIN_RIGHT,
      });

    // Generation timestamp (right aligned)
    const genTime = new Date().toLocaleString('en-US', {
      year: 'numeric',
      month: 'short',
      day: 'numeric',
      hour: '2-digit',
      minute: '2-digit',
    });
    doc.fontSize(this.FONT_SMALL)
      .text(`Generated: ${genTime}`, this.MARGIN_LEFT, 55, {
        width: doc.page.width - this.MARGIN_LEFT - this.MARGIN_RIGHT,
        align: 'right',
      });

    // Reset color and position
    doc.fillColor('#000000');
    doc.y = this.HEADER_HEIGHT + 20;
  }

  /**
   * Draw page footer with branding and page numbers
   */
  private static drawFooter(doc: any, pageNumber: number, totalPages: number) {
    const footerY = doc.page.height - this.FOOTER_HEIGHT;

    // Footer line
    doc.strokeColor('#E5E7EB')
      .lineWidth(1)
      .moveTo(this.MARGIN_LEFT, footerY)
      .lineTo(doc.page.width - this.MARGIN_RIGHT, footerY)
      .stroke();

    // Page number
    doc.fontSize(this.FONT_SMALL)
      .font('Helvetica')
      .fillColor('#6B7280')
      .text(
        `Page ${pageNumber} of ${totalPages}`,
        this.MARGIN_LEFT,
        footerY + 10,
        {
          width: doc.page.width - this.MARGIN_LEFT - this.MARGIN_RIGHT,
          align: 'center',
        }
      );

    // Company branding
    doc.text(
      'Quick Connect - System Monitoring & Logging',
      this.MARGIN_LEFT,
      footerY + 25,
      {
        width: doc.page.width - this.MARGIN_LEFT - this.MARGIN_RIGHT,
        align: 'center',
      }
    );

    doc.fillColor('#000000');
  }

  /**
   * Draw horizontal bar chart for statistics
   */
  private static drawBarChart(
    doc: any,
    label: string,
    value: number,
    maxValue: number,
    color: string,
    x: number,
    y: number,
    width: number,
    height: number = 12
  ): number {
    const barWidth = (value / maxValue) * (width - 100);
    const labelWidth = 80;

    // Label
    doc.fontSize(this.FONT_BODY)
      .font('Helvetica')
      .fillColor('#000000')
      .text(label, x, y, { width: labelWidth });

    // Bar background
    doc.rect(x + labelWidth + 10, y + 2, width - labelWidth - 10, height)
      .fill(this.BRAND_BG_BLUE);

    // Bar fill
    if (barWidth > 0) {
      doc.rect(x + labelWidth + 10, y + 2, barWidth, height)
        .fill(color);
    }

    // Value text
    doc.fontSize(this.FONT_BODY)
      .font('Helvetica-Bold')
      .fillColor('#000000')
      .text(value.toString(), x + labelWidth + 15 + barWidth, y, {
        width: 50,
        align: 'left',
      });

    return y + height + 8;
  }

  /**
   * Analyze logs for critical alerts and recommendations
   */
  private static analyzeCriticalAlerts(logs: any[]): Array<{
    severity: string;
    module: string;
    count: number;
    recommendation: string;
  }> {
    const alerts: Array<{
      severity: string;
      module: string;
      count: number;
      recommendation: string;
    }> = [];

    // Group by module and level
    const moduleErrors: Record<string, { ERROR: number; CRITICAL: number }> = {};

    logs.forEach((log) => {
      if (log.level === 'ERROR' || log.level === 'CRITICAL') {
        if (!moduleErrors[log.module]) {
          moduleErrors[log.module] = { ERROR: 0, CRITICAL: 0 };
        }
        moduleErrors[log.module][log.level as 'ERROR' | 'CRITICAL']++;
      }
    });

    // Generate recommendations
    Object.entries(moduleErrors).forEach(([module, counts]) => {
      const total = counts.ERROR + counts.CRITICAL;
      if (total >= 5) {
        alerts.push({
          severity: counts.CRITICAL > 0 ? 'CRITICAL' : 'ERROR',
          module,
          count: total,
          recommendation:
            counts.CRITICAL > 0
              ? `Immediate investigation required. ${counts.CRITICAL} critical error(s) detected. Review error logs, check system health, and verify service dependencies.`
              : `High error rate detected (${total} errors). Review recent changes, check dependencies, and monitor for patterns.`,
        });
      }
    });

    return alerts.sort((a, b) => {
      if (a.severity === 'CRITICAL' && b.severity !== 'CRITICAL') return -1;
      if (a.severity !== 'CRITICAL' && b.severity === 'CRITICAL') return 1;
      return b.count - a.count;
    });
  }

  /**
   * Generate PDF report for admin
   * GET /api/logging/detail-pdf
   */
  public static generatePDFReport = asyncHandler(
    async (req: AuthenticatedRequest, res: Response) => {
      const { module, level, startDate, endDate } = req.query;

      const adminId = '694564d876661ff3f9b4887f';

      // Build query parameters
      const params: any = {
        admin_id: adminId,
        page: 1,
        limit: 1000, // Get more logs for PDF
      };

      if (module && typeof module === 'string') {
        params.module = module;
      }

      if (
        level &&
        typeof level === 'string' &&
        ['INFO', 'WARN', 'ERROR', 'DEBUG', 'CRITICAL'].includes(level)
      ) {
        params.level = level as LogLevel;
      }

      if (startDate) {
        params.startDate = new Date(startDate as string);
      }

      if (endDate) {
        params.endDate = new Date(endDate as string);
      }

      // Get logs
      const result = await LoggingService.getLogsByAdminId(adminId, params);
      const logs = result.logs;

      // Get statistics
      const statistics = await LoggingService.getStatistics({
        startDate: params.startDate,
        endDate: params.endDate,
        module: params.module,
      });

      // Create PDF document
      const doc = new PDFDocument({
        size: 'A4',
        margins: {
          top: this.MARGIN_TOP,
          bottom: this.MARGIN_BOTTOM,
          left: this.MARGIN_LEFT,
          right: this.MARGIN_RIGHT,
        },
      });

      // Set response headers
      res.setHeader('Content-Type', 'application/pdf');
      res.setHeader(
        'Content-Disposition',
        `attachment; filename="logging-report-${new Date().toISOString().split('T')[0]}.pdf"`
      );

      // Pipe PDF to response
      doc.pipe(res);

      // Track page numbers for TOC
      const tocEntries: Array<{ title: string; page: number }> = [];
      let currentPage = 1;

      // Helper to add page and update TOC
      const addPageWithHeader = (isFirstPage: boolean = false) => {
        if (!isFirstPage) {
          doc.addPage();
          currentPage++;
        }
        const pageRange = doc.bufferedPageRange();
        const totalPages = pageRange.count;
        this.drawHeader(doc, currentPage, totalPages, isFirstPage);
      };

      // First page header
      addPageWithHeader(true);
      const pageRange = doc.bufferedPageRange();
      let totalPages = pageRange.count;

      // Executive Summary Section
      doc.fontSize(this.FONT_SECTION)
        .font('Helvetica-Bold')
        .fillColor(this.BRAND_BLUE)
        .text('Executive Summary', this.MARGIN_LEFT, doc.y, {
          underline: true,
        });
      tocEntries.push({ title: 'Executive Summary', page: currentPage });
      doc.moveDown(1.5);

      // Summary metrics card
      const cardY = doc.y;
      const cardHeight = 80;
      doc.rect(this.MARGIN_LEFT, cardY, doc.page.width - this.MARGIN_LEFT - this.MARGIN_RIGHT, cardHeight)
        .fill(this.BRAND_BG_BLUE)
        .stroke(this.BRAND_LIGHT_BLUE);

      doc.fontSize(this.FONT_SUBSECTION)
        .font('Helvetica-Bold')
        .fillColor(this.BRAND_BLUE)
        .text('Report Overview', this.MARGIN_LEFT + 15, cardY + 10);

      doc.fontSize(this.FONT_BODY)
        .font('Helvetica')
        .fillColor('#000000')
        .text(`Total Logs: ${statistics.total}`, this.MARGIN_LEFT + 15, cardY + 35);

      const dateRange = params.startDate && params.endDate
        ? `${new Date(params.startDate).toLocaleDateString()} - ${new Date(params.endDate).toLocaleDateString()}`
        : 'All Time';
      doc.text(`Date Range: ${dateRange}`, this.MARGIN_LEFT + 200, cardY + 35);

      if (params.module) {
        doc.text(`Module Filter: ${params.module}`, this.MARGIN_LEFT + 15, cardY + 55);
      }
      if (params.level) {
        doc.text(`Level Filter: ${params.level}`, this.MARGIN_LEFT + 200, cardY + 55);
      }

      doc.y = cardY + cardHeight + 20;

      // Statistics by Level with bar charts
      if (statistics.byLevel && statistics.byLevel.length > 0) {
        doc.fontSize(this.FONT_SUBSECTION)
          .font('Helvetica-Bold')
          .fillColor(this.BRAND_BLUE)
          .text('Logs by Level', this.MARGIN_LEFT, doc.y);
        doc.moveDown(0.8);

        const maxCount = Math.max(...statistics.byLevel.map((s: any) => s.count));
        let chartY = doc.y;

        const levelColors: Record<string, string> = {
          CRITICAL: this.CRITICAL_DARK_RED,
          ERROR: this.ERROR_RED,
          WARN: this.WARN_ORANGE,
          INFO: this.INFO_BLUE,
          DEBUG: this.DEBUG_GRAY,
        };

        statistics.byLevel.forEach((stat: any) => {
          chartY = this.drawBarChart(
            doc,
            stat.level,
            stat.count,
            maxCount,
            levelColors[stat.level] || this.DEBUG_GRAY,
            this.MARGIN_LEFT,
            chartY,
            doc.page.width - this.MARGIN_LEFT - this.MARGIN_RIGHT
          );
        });

        doc.y = chartY + 10;
      }

      // Top Modules with visual indicators
      if (statistics.byModule && statistics.byModule.length > 0) {
        doc.fontSize(this.FONT_SUBSECTION)
          .font('Helvetica-Bold')
          .fillColor(this.BRAND_BLUE)
          .text('Top Modules by Activity', this.MARGIN_LEFT, doc.y);
        doc.moveDown(0.8);

        statistics.byModule.slice(0, 10).forEach((stat: any, index: number) => {
          const moduleY = doc.y;
          const badgeColor = index < 3 ? this.BRAND_LIGHT_BLUE : this.BRAND_BG_BLUE;

          // Rank badge
          doc.circle(this.MARGIN_LEFT + 10, moduleY + 5, 8)
            .fill(badgeColor)
            .stroke(this.BRAND_BLUE);

          doc.fontSize(this.FONT_SMALL)
            .font('Helvetica-Bold')
            .fillColor(index < 3 ? '#FFFFFF' : this.BRAND_BLUE)
            .text((index + 1).toString(), this.MARGIN_LEFT + 6, moduleY + 2, {
              width: 8,
              align: 'center',
            });

          // Module name and count
          doc.fontSize(this.FONT_BODY)
            .font('Helvetica')
            .fillColor('#000000')
            .text(stat._id, this.MARGIN_LEFT + 25, moduleY, { width: 300 })
            .font('Helvetica-Bold')
            .text(stat.count.toString(), doc.page.width - this.MARGIN_RIGHT - 50, moduleY, {
              width: 50,
              align: 'right',
            });

          doc.y = moduleY + 18;
        });

        doc.moveDown(1);
      }

      // Critical Alerts & Recommended Actions
      const criticalAlerts = this.analyzeCriticalAlerts(logs);
      if (criticalAlerts.length > 0) {
        // Check if we need a new page
        if (doc.y > doc.page.height - this.FOOTER_HEIGHT - 150) {
          addPageWithHeader();
          totalPages = doc.bufferedPageRange().count;
        }

        doc.fontSize(this.FONT_SECTION)
          .font('Helvetica-Bold')
          .fillColor(this.BRAND_BLUE)
          .text('Critical Alerts & Recommended Actions', this.MARGIN_LEFT, doc.y, {
            underline: true,
          });
        tocEntries.push({ title: 'Critical Alerts', page: currentPage });
        doc.moveDown(1);

        criticalAlerts.forEach((alert) => {
          const alertY = doc.y;
          const alertHeight = 60;
          const alertColor = alert.severity === 'CRITICAL' ? this.CRITICAL_DARK_RED : this.ERROR_RED;

          // Alert box
          doc.rect(this.MARGIN_LEFT, alertY, doc.page.width - this.MARGIN_LEFT - this.MARGIN_RIGHT, alertHeight)
            .fill('#FEF2F2')
            .stroke(alertColor);

          // Severity badge
          doc.circle(this.MARGIN_LEFT + 15, alertY + 15, 10)
            .fill(alertColor);

          doc.fontSize(this.FONT_SMALL)
            .font('Helvetica-Bold')
            .fillColor('#FFFFFF')
            .text('!', this.MARGIN_LEFT + 10, alertY + 11, { width: 10, align: 'center' });

          // Alert content
          doc.fontSize(this.FONT_SUBSECTION)
            .font('Helvetica-Bold')
            .fillColor(alertColor)
            .text(`${alert.severity}: ${alert.module}`, this.MARGIN_LEFT + 35, alertY + 5)
            .fontSize(this.FONT_SMALL)
            .font('Helvetica-Bold')
            .fillColor('#6B7280')
            .text(`${alert.count} error(s) detected`, this.MARGIN_LEFT + 35, alertY + 22);

          doc.fontSize(this.FONT_BODY)
            .font('Helvetica')
            .fillColor('#000000')
            .text(alert.recommendation, this.MARGIN_LEFT + 15, alertY + 38, {
              width: doc.page.width - this.MARGIN_LEFT - this.MARGIN_RIGHT - 30,
            });

          doc.y = alertY + alertHeight + 15;
        });

        doc.moveDown(1);
      }

      // Filter Criteria
      if (params.module || params.level || params.startDate || params.endDate) {
        if (doc.y > doc.page.height - this.FOOTER_HEIGHT - 100) {
          addPageWithHeader();
          totalPages = doc.bufferedPageRange().count;
        }

        doc.fontSize(this.FONT_SUBSECTION)
          .font('Helvetica-Bold')
          .fillColor(this.BRAND_BLUE)
          .text('Filter Criteria', this.MARGIN_LEFT, doc.y);
        doc.moveDown(0.8);

        doc.fontSize(this.FONT_BODY)
          .font('Helvetica')
          .fillColor('#000000');

        const filterY = doc.y;
        let filterX = this.MARGIN_LEFT;
        let filterLine = 0;

        if (params.module) {
          doc.text(`Module: ${params.module}`, filterX, filterY + filterLine * 15, { width: 250 });
          filterX += 260;
          if (filterX > doc.page.width - this.MARGIN_RIGHT) {
            filterX = this.MARGIN_LEFT;
            filterLine++;
          }
        }

        if (params.level) {
          doc.text(`Level: ${params.level}`, filterX, filterY + filterLine * 15, { width: 250 });
          filterX += 260;
          if (filterX > doc.page.width - this.MARGIN_RIGHT) {
            filterX = this.MARGIN_LEFT;
            filterLine++;
          }
        }

        if (params.startDate) {
          doc.text(
            `Start: ${new Date(params.startDate).toLocaleString()}`,
            filterX,
            filterY + filterLine * 15,
            { width: 250 }
          );
          filterX += 260;
          if (filterX > doc.page.width - this.MARGIN_RIGHT) {
            filterX = this.MARGIN_LEFT;
            filterLine++;
          }
        }

        if (params.endDate) {
          doc.text(
            `End: ${new Date(params.endDate).toLocaleString()}`,
            filterX,
            filterY + filterLine * 15,
            { width: 250 }
          );
        }

        doc.y = filterY + (filterLine + 1) * 15 + 20;
      }

      // Log Entries Table
      if (doc.y > doc.page.height - this.FOOTER_HEIGHT - 100) {
        addPageWithHeader();
        totalPages = doc.bufferedPageRange().count;
      }

      doc.fontSize(this.FONT_SECTION)
        .font('Helvetica-Bold')
        .fillColor(this.BRAND_BLUE)
        .text('Detailed Log Entries', this.MARGIN_LEFT, doc.y, {
          underline: true,
        });
      tocEntries.push({ title: 'Detailed Log Entries', page: currentPage });
      doc.moveDown(1);

      // Table setup
      const tableTop = doc.y;
      const tableLeft = this.MARGIN_LEFT;
      const colWidths = [30, 70, 90, 220, 120];
      const rowHeight = 22;
      const tableWidth = colWidths.reduce((a, b) => a + b, 0) + 20;

      // Table header with blue background
      doc.rect(tableLeft, tableTop, tableWidth, rowHeight)
        .fill(this.BRAND_BLUE)
        .stroke(this.BRAND_BLUE);

      doc.fontSize(this.FONT_TABLE)
        .font('Helvetica-Bold')
        .fillColor('#FFFFFF');

      let headerX = tableLeft + 5;
      doc.text('#', headerX, tableTop + 6);
      headerX += colWidths[0];
      doc.text('Level', headerX, tableTop + 6);
      headerX += colWidths[1];
      doc.text('Module', headerX, tableTop + 6);
      headerX += colWidths[2];
      doc.text('Action', headerX, tableTop + 6);
      headerX += colWidths[3];
      doc.text('Timestamp', headerX, tableTop + 6);

      doc.fillColor('#000000');
      let yPosition = tableTop + rowHeight;

      // Log entries with alternating row colors
      logs.forEach((log: any, index: number) => {
        // Check if we need a new page
        if (yPosition > doc.page.height - this.FOOTER_HEIGHT - rowHeight) {
          addPageWithHeader();
          totalPages = doc.bufferedPageRange().count;
          // Redraw table header on new page
          doc.rect(tableLeft, doc.y, tableWidth, rowHeight)
            .fill(this.BRAND_BLUE)
            .stroke(this.BRAND_BLUE);
          doc.fontSize(this.FONT_TABLE)
            .font('Helvetica-Bold')
            .fillColor('#FFFFFF');
          headerX = tableLeft + 5;
          doc.text('#', headerX, doc.y + 6);
          headerX += colWidths[0];
          doc.text('Level', headerX, doc.y + 6);
          headerX += colWidths[1];
          doc.text('Module', headerX, doc.y + 6);
          headerX += colWidths[2];
          doc.text('Action', headerX, doc.y + 6);
          headerX += colWidths[3];
          doc.text('Timestamp', headerX, doc.y + 6);
          doc.fillColor('#000000');
          yPosition = doc.y + rowHeight;
        }

        // Alternating row background
        if (index % 2 === 1) {
          doc.rect(tableLeft, yPosition, tableWidth, rowHeight)
            .fill(this.ROW_ALT_COLOR);
        }

        // Row number
        doc.fontSize(this.FONT_TABLE)
          .font('Helvetica')
          .fillColor('#6B7280')
          .text((index + 1).toString(), tableLeft + 5, yPosition + 6, {
            width: colWidths[0],
            align: 'center',
          });

        // Level badge
        const levelColors: Record<string, string> = {
          ERROR: this.ERROR_RED,
          CRITICAL: this.CRITICAL_DARK_RED,
          WARN: this.WARN_ORANGE,
          INFO: this.INFO_BLUE,
          DEBUG: this.DEBUG_GRAY,
        };

        const levelColor = levelColors[log.level] || this.DEBUG_GRAY;
        const levelX = tableLeft + colWidths[0] + 5;
        const levelY = yPosition + 4;

        doc.rect(levelX, levelY, 50, 14)
          .fill(levelColor)
          .stroke(levelColor);

        doc.fontSize(this.FONT_TABLE)
          .font('Helvetica-Bold')
          .fillColor('#FFFFFF')
          .text(log.level || 'N/A', levelX + 2, levelY + 3, {
            width: 46,
            align: 'center',
          });

        // Module
        doc.fontSize(this.FONT_TABLE)
          .font('Helvetica')
          .fillColor('#000000')
          .text((log.module || 'N/A').substring(0, 15), tableLeft + colWidths[0] + colWidths[1] + 5, yPosition + 6, {
            width: colWidths[2],
          });

        // Action (with proper truncation)
        const actionText = (log.action || 'N/A').substring(0, 50);
        doc.text(actionText, tableLeft + colWidths[0] + colWidths[1] + colWidths[2] + 5, yPosition + 6, {
          width: colWidths[3],
          ellipsis: true,
        });

        // Timestamp
        const timestamp = log.timestamp
          ? new Date(log.timestamp).toLocaleString('en-US', {
            month: 'short',
            day: 'numeric',
            hour: '2-digit',
            minute: '2-digit',
          })
          : 'N/A';
        doc.text(timestamp, tableLeft + colWidths[0] + colWidths[1] + colWidths[2] + colWidths[3] + 5, yPosition + 6, {
          width: colWidths[4],
        });

        // Row border
        doc.strokeColor('#E5E7EB')
          .lineWidth(0.5)
          .moveTo(tableLeft, yPosition + rowHeight)
          .lineTo(tableLeft + tableWidth, yPosition + rowHeight)
          .stroke();

        yPosition += rowHeight;
      });

      // Add Table of Contents if report is 3+ pages
      if (totalPages >= 3 && tocEntries.length > 1) {
        // Insert TOC after first page header
        const firstPageRange = doc.bufferedPageRange();
        doc.switchToPage(0);
        const tocY = this.HEADER_HEIGHT + 40;

        doc.fontSize(this.FONT_SECTION)
          .font('Helvetica-Bold')
          .fillColor(this.BRAND_BLUE)
          .text('Table of Contents', this.MARGIN_LEFT, tocY, {
            underline: true,
          });

        let tocItemY = tocY + 30;
        tocEntries.forEach((entry) => {
          doc.fontSize(this.FONT_BODY)
            .font('Helvetica')
            .fillColor('#000000')
            .text(entry.title, this.MARGIN_LEFT, tocItemY, { width: 400 })
            .fillColor(this.BRAND_BLUE)
            .text(`... ${entry.page}`, this.MARGIN_LEFT + 400, tocItemY, {
              width: 100,
              align: 'right',
            });
          tocItemY += 20;
        });
      }

      // Add footers to all pages
      const finalPageRange = doc.bufferedPageRange();
      for (let i = 0; i < finalPageRange.count; i++) {
        doc.switchToPage(i);
        this.drawFooter(doc, i + 1, finalPageRange.count);
      }

      // Finalize PDF
      doc.end();
    }
  );
}
