class Template1Generator {
    constructor() {
        this.pageWidth = 215.9;
        this.pageHeight = 304.8;
        this.margin = 12.7;
        this.lineHeight = 5;
        this.currentY = this.margin;

        this.headerHeight = 20;
        this.footerHeight = 15;
        this.headerFooterMargin = 6.35;

        this.demandHeaders = {
            physicalDemands: {
                columns: ['N/A <1%', 'Occasional 1-33%', 'Frequent 34-66%', 'Constant 67-100%', 'Comments'],
                rows: [
                    'Awkward position', 'Bending over', 'Carrying', 'Driving', 'Fine motor tasks',
                    'Gripping or grasping', 'Handling', 'Kneeling', 'Lifting', 'Lifting overhead',
                    'Pulling', 'Pushing', 'Reaching', 'Sitting', 'Squatting or crouching',
                    'Standing', 'Talking and hearing', 'Twisting or turning', 'Walking'
                ]
            },
            mobilityDemands: {
                columns: ['N/A <1%', 'Occasional 1-33%', 'Frequent 34-66%', 'Constant 67-100%', 'Comments'],
                rows: [
                    'Flexion/Extension', 'Rotation', 'Lateral Flexion/Extension',
                    'Flexion/Extension', 'Abduction/Adduction', 'Internal/External Rotation', 'Elevation/Depression',
                    'Flexion/Extension', 'Supination/Pronation',
                    'Flexion/Extension', 'Ulnar/Radial Deviation', 'Gripping (Power or Pinch)',
                    'Flexion/Extension', 'Rotation', 'Lateral Flexion/Extension',
                    'Flexion/Extension', 'Rotation', 'Lateral Flexion/Extension',
                    'Flexion/Extension', 'Abduction/Adduction', 'Internal/External Rotation',
                    'Flexion/Extension',
                    'Dorsiflexion/Plantarflexion'
                ],
                rowGroups: [
                    { category: 'Neck', rows: [0, 1, 2] },
                    { category: 'Shoulder', rows: [3, 4, 5, 6] },
                    { category: 'Elbow and Forearm', rows: [7, 8] },
                    { category: 'Wrist and Fingers', rows: [9, 10, 11] },
                    { category: 'Thorax and Upper Back', rows: [12, 13, 14] },
                    { category: 'Lower Back and Abdomen', rows: [15, 16, 17] },
                    { category: 'Hip and Upper Thigh', rows: [18, 19, 20] },
                    { category: 'Knees and Lower Legs', rows: [21] },
                    { category: 'Ankle, Foot, and Toes', rows: [22] }
                ]
            },
            cognitiveSensoryDemands: {
                columns: ['Required', 'Comments'],
                rows: [
                    'Near Vision', 'Far Vision', 'Peripheral Vision', 'Depth Perception', 'Color Vision',
                    'Perceive Safety/Emergency Indicators',
                    'Distinguish Sounds or Tones', 'Verbal or Electronic Communication', 'Perceive Safety/Emergency Indicators',
                    'Tactile Sense (Touch)', 'Olfactory Sense (Smell)', 'Gustatory Sense (Taste)',
                    'Vestibular Sense (Balance)', 'Kinesthetic Sense (Proprioception)',
                    'Memory (Short or Long Term)', 'Multitasking', 'Decision Making and Reasoning',
                    'Simple Math', 'Time Management', 'Literacy (Reading/Writing)',
                    'Work Independently', 'Work with a Team', 'Supervision of Others'
                ],
                rowGroups: [
                    { category: 'Vision', rows: [0, 1, 2, 3, 4, 5] },
                    { category: 'Hearing', rows: [6, 7, 8] },
                    { category: 'Senses', rows: [9, 10, 11, 12, 13] },
                    { category: 'Cognitive', rows: [14, 15, 16, 17, 18, 19] },
                    { category: 'Psychosocial', rows: [20, 21, 22] }
                ]
            },
            environmentalDemands: {
                columns: ['N/A <1%', 'Occasional 1-33%', 'Frequent 34-66%', 'Constant 67-100%', 'Comments'],
                rows: [
                    'Wet, humid, or slippery surfaces', 'Proximity to moving mechanical parts or machinery',
                    'Working at heights', 'Fumes, odors, dust, or airborne particles',
                    'Hazardous chemicals (toxic or caustic)', 'Extreme temperatures (hot or cold, weather-related or non-weather)',
                    'High noise levels requiring hearing protection', 'Hand-arm vibration (e.g., from power tools)',
                    'Whole-body vibration (e.g., from vehicles or platforms)', 'Electrical hazards',
                    'Radiation exposure (ionizing or non-ionizing)', 'Poor lighting or illumination',
                    'Confined spaces', 'Biological hazards (e.g., pathogens or allergens)'
                ]
            },
            liftingPushingPulling: {
                columns: ['N/A, <1%', 'Occasional 1-33%, <12 reps/hour', 'Frequent 34-66%, 12-60 reps/hour', 'Constant 67-100%, >60 reps/hour', 'Comments'],
                rows: ['Less than 5 lbs', '5-25 lbs', '26-50 lbs', '51-100 lbs', 'Over 100 lbs']
            }
        };
    }

    generate(data) {
        const jsPDF = window.jspdf.jsPDF;

        this.doc = new jsPDF({
            orientation: 'portrait',
            unit: 'mm',
            format: [215.9, 304.8]
        });
        this.data = data;
        this.currentY = this.margin;

        this.addCoverPage();
        this.addJobOverview();
        this.addJobFunctions();
        this.addWorkSchedule();
        this.addJobImages();
        this.addJobDemands();
        this.addSummary();

        HeaderFooterUtils.addHeadersAndFooters(this.doc, this.data, {
            pageWidth: this.pageWidth,
            pageHeight: this.pageHeight,
            margin: this.margin,
            headerHeight: 20,
            footerHeight: 15
        });

        return this.doc;
    }

    addCoverPage() {
        const brandColorRgb = PDFUtils.hexToRgb(this.data.brandColor || '#003366');
        const secondaryColorRgb = PDFUtils.hexToRgb(this.data.secondaryBrandColor || '#047857');

        this.currentY = 30;

        this.doc.setTextColor(...brandColorRgb);
        this.doc.setFontSize(32);
        this.doc.setFont(undefined, 'bold');
        const titleLines = this.doc.splitTextToSize(this.data.title || 'Job Analysis Report', this.pageWidth - (this.margin * 2));
        this.doc.text(titleLines, this.margin, this.currentY);
        this.currentY += (titleLines.length * 10) + 15;

        this.doc.setTextColor(0, 0, 0);

        this.doc.setFontSize(11);
        this.doc.setFont(undefined, 'bold');
        this.doc.setTextColor(...secondaryColorRgb);
        this.doc.text('Position:', this.margin, this.currentY);
        this.doc.setTextColor(0, 0, 0);
        this.doc.setFont(undefined, 'normal');
        this.doc.text(this.data.jobTitle || 'Not specified', this.margin + 35, this.currentY);
        this.currentY += 10;

        this.doc.setFont(undefined, 'bold');
        this.doc.setTextColor(...secondaryColorRgb);
        this.doc.text('Company:', this.margin, this.currentY);
        this.doc.setTextColor(0, 0, 0);
        this.doc.setFont(undefined, 'normal');
        this.doc.text(this.data.companyName || 'Not specified', this.margin + 35, this.currentY);
        this.currentY += 8;

        const address = this.formatAddress();
        if (address) {
            this.doc.setFont(undefined, 'bold');
            this.doc.setTextColor(...secondaryColorRgb);
            this.doc.text('Address:', this.margin, this.currentY);
            this.doc.setTextColor(0, 0, 0);
            this.doc.setFont(undefined, 'normal');
            this.doc.text(address, this.margin + 35, this.currentY);
            this.currentY += 8;
        }

        this.currentY += 12;
        this.doc.setFontSize(10);
        this.doc.setFont(undefined, 'bold');
        this.doc.setTextColor(...brandColorRgb);
        this.doc.text('Analysis Information', this.margin, this.currentY);

        this.doc.setDrawColor(...secondaryColorRgb);
        this.doc.setLineWidth(0.5);
        this.doc.line(this.margin, this.currentY + 2, this.pageWidth - this.margin, this.currentY + 2);
        this.currentY += 8;

        this.doc.setFont(undefined, 'normal');
        this.doc.setFontSize(9);

        this.doc.setFont(undefined, 'bold');
        this.doc.setTextColor(...secondaryColorRgb);
        this.doc.text('Analyst:', this.margin, this.currentY);
        this.doc.setTextColor(0, 0, 0);
        this.doc.setFont(undefined, 'normal');
        this.doc.text(this.data.author || 'Not specified', this.margin + 35, this.currentY);
        this.currentY += 6;

        this.doc.setFont(undefined, 'bold');
        this.doc.setTextColor(...secondaryColorRgb);
        this.doc.text('Email:', this.margin, this.currentY);
        this.doc.setTextColor(0, 0, 0);
        this.doc.setFont(undefined, 'normal');
        this.doc.text(this.data.email || 'Not specified', this.margin + 35, this.currentY);
        this.currentY += 6;

        this.doc.setFont(undefined, 'bold');
        this.doc.setTextColor(...secondaryColorRgb);
        this.doc.text('Analysis Date:', this.margin, this.currentY);
        this.doc.setTextColor(0, 0, 0);
        this.doc.setFont(undefined, 'normal');
        this.doc.text(this.formatDate(this.data.date), this.margin + 35, this.currentY);

        this.addPageBreak();
    }

    formatDate(dateString) {
        if (!dateString) {
            return new Date().toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' });
        }

        try {

            if (typeof dateString === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(dateString)) {
                const [year, month, day] = dateString.split('-').map(Number);
                const date = new Date(year, month - 1, day);
                return date.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' });
            }

            const date = new Date(dateString);
            return date.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' });
        } catch (e) {
            return dateString;
        }
    }

    formatAddress() {
        const parts = [];
        if (this.data.companyStreet) parts.push(this.data.companyStreet);
        if (this.data.companyCity) parts.push(this.data.companyCity);
        if (this.data.companyState) parts.push(this.data.companyState);
        if (this.data.companyZip) parts.push(this.data.companyZip);
        return parts.join(', ');
    }

    addJobOverview() {
        this.addSectionHeader('Overview');

        this.addField('Job Purpose', this.data.jobPurpose);

        this.checkPageBreak(20);
    }

    addJobFunctions() {

        this.addField('Essential Functions', this.data.essentialFunctions);
        this.addField('Marginal Functions', this.data.marginalFunctions);

        this.checkPageBreak(20);
    }

    addWorkSchedule() {
        this.addField('Work Schedule', this.data.workSchedule);
        this.addField('Breaks', this.data.breaks);
        this.addField('Other Shift Information', this.data.otherShiftInfo);

        this.addPageBreak();
    }

    addJobImages() {

        if (!this.data.jobImages || this.data.jobImages.length <= 1) {
            return;
        }

        this.addSectionHeader('Images');

        const images = this.data.jobImages;

        const pageContentWidth = this.pageWidth - (this.margin * 2);
        const pageContentHeight = this.pageHeight - (this.margin * 2) - this.headerHeight - this.footerHeight;

        const imagesPerRow = 3;
        const imagesPerColumn = 4;
        const horizontalGap = 6;
        const verticalGap = 6;

        const boxWidth = (pageContentWidth - (horizontalGap * (imagesPerRow - 1))) / imagesPerRow;

        const boxHeight = (pageContentHeight - (verticalGap * (imagesPerColumn - 1))) / imagesPerColumn;

        const boxSize = Math.min(boxWidth, boxHeight);

        const imagesToRender = Math.min(12, images.length);

        for (let i = 0; i < imagesToRender; i++) {
            const image = images[i];
            const rowIndex = Math.floor(i / imagesPerRow);
            const colIndex = i % imagesPerRow;

            const xPos = this.margin + (colIndex * (boxSize + horizontalGap));
            const yPos = this.currentY + (rowIndex * (boxSize + verticalGap));

            try {
                this.doc.addImage(image.preview, 'JPEG', xPos, yPos, boxSize, boxSize);
            } catch (e) {

                this.doc.setDrawColor(200, 200, 200);
                this.doc.rect(xPos, yPos, boxSize, boxSize);
                this.doc.setFontSize(8);
                this.doc.setTextColor(150, 150, 150);
                this.doc.text('Image', xPos + (boxSize / 2), yPos + (boxSize / 2), { align: 'center' });
            }
        }

        this.addPageBreak();
    }

    addJobDemands() {
        this.addSectionHeader('Job Demands');

        this.addDemandTable('Physical Demands', this.data.physicalDemands);
        this.addDemandTable('Mobility Demands', this.data.mobilityDemands);
        this.addDemandTable('Cognitive/Sensory Demands', this.data.cognitiveSensoryDemands);
        this.addDemandTable('Environmental Demands', this.data.environmentalDemands);
        this.addDemandTable('Lifting/Pushing/Pulling', this.data.liftingPushingPulling);

        this.checkPageBreak(20);
    }

    addSummary() {

        this.addPageBreak();

        this.addSectionHeader('Summary');

        this.addClassificationResult();

        this.addField('Analysis Notes', this.data.summaryText);
    }

    addClassificationResult() {

        const levelColor = this.getLevelColor();

        const cfrDef = this.getCFRDefinition();

        const boxPadding = 4;
        const boxX = this.margin;
        const boxY = this.currentY;
        const boxWidth = this.pageWidth - (this.margin * 2);
        const PT_TO_MM = 25.4 / 72;
        const lineHeightFactor = 1.15;

        const spacingBeforeClassification = 2;
        const spacingAfterClassification = 3;
        const spacingAfterTitle = 2;

        let contentHeight = 0;
        const classFontSize = 11;
        const classLineHeight = classFontSize * lineHeightFactor * PT_TO_MM;
        contentHeight += spacingBeforeClassification;
        contentHeight += classLineHeight;

        let definitionLines = [];
        if (cfrDef) {
            contentHeight += spacingAfterClassification;

            const titleFontSize = 11;
            const titleLineHeight = titleFontSize * lineHeightFactor * PT_TO_MM;
            contentHeight += titleLineHeight;
            contentHeight += spacingAfterTitle;

            const defFontSize = 10;
            this.doc.setFontSize(defFontSize);
            const defWidth = boxWidth - (boxPadding * 2);
            definitionLines = this.doc.splitTextToSize(cfrDef.definition, defWidth);
            const defLineHeight = defFontSize * lineHeightFactor * PT_TO_MM;
            contentHeight += definitionLines.length * defLineHeight;
        }

        const boxHeight = contentHeight + (boxPadding * 2);

        this.doc.setDrawColor(...levelColor);
        this.doc.setLineWidth(1.0);
        this.doc.rect(boxX, boxY, boxWidth, boxHeight);

        this.doc.setTextColor(...levelColor);
        this.doc.setFontSize(classFontSize);

        const textY = boxY + boxPadding + spacingBeforeClassification;

        const classificationText = this.formatClassification();
        const parts = classificationText.split(': ');
        let xPos = boxX + boxPadding;

        this.doc.setFont(undefined, 'normal');
        this.doc.text(parts[0] + ':', xPos, textY);
        xPos += this.doc.getTextWidth(parts[0] + ': ');

        this.doc.setFont(undefined, 'bold');
        const formattedLevel = this.formatPhysicalLevel(parts[1]);
        this.doc.text(formattedLevel.toUpperCase(), xPos, textY);

        if (cfrDef) {
            let currentY = textY + classLineHeight + spacingAfterClassification;

            const titleFontSize = 11;
            this.doc.setFontSize(titleFontSize);
            this.doc.setFont(undefined, 'bold');
            this.doc.setTextColor(0, 0, 0);
            this.doc.text(`${cfrDef.code} - ${cfrDef.title}`, boxX + boxPadding, currentY);
            const titleLineHeight = titleFontSize * lineHeightFactor * PT_TO_MM;
            currentY += titleLineHeight + spacingAfterTitle;

            this.doc.setFont(undefined, 'normal');
            const defFontSize = 10;
            this.doc.setFontSize(defFontSize);
            this.doc.text(definitionLines, boxX + boxPadding, currentY);
        }

        this.currentY += boxHeight + 6;
    }

    formatPhysicalLevel(level) {
        if (!level) return '';
        return level.replace(/_/g, ' ');
    }

    getCFRDefinition() {
        if (!this.data.classificationOfWork || !this.data.classificationOfWork.physicalLevel) {
            return null;
        }

        return window.CFRDefinitions.getDefinition(this.data.classificationOfWork.physicalLevel);
    }

    getLevelColor() {
        if (!this.data.classificationOfWork || !this.data.classificationOfWork.physicalLevel) {
            return PDFUtils.hexToRgb('#6c757d');
        }

        const level = this.data.classificationOfWork.physicalLevel.toLowerCase();

        const colorMap = {
            'sedentary': '#10b981',
            'light': '#3b82f6',
            'medium': '#f59e0b',
            'heavy': '#ef4444',
            'very_heavy': '#6c757d',
            'very heavy': '#6c757d'
        };

        const hexColor = colorMap[level] || '#6c757d';
        return PDFUtils.hexToRgb(hexColor);
    }

    formatClassification() {
        if (!this.data.classificationOfWork) return 'Not specified';
        const c = this.data.classificationOfWork;
        const formattedLevel = this.formatPhysicalLevel(c.physicalLevel || 'Not specified');
        return `Physical Classification of Work: ${formattedLevel}`;
    }

    addSectionHeader(title) {
        this.checkPageBreak(15);
        this.doc.setFontSize(12);
        this.doc.setFont(undefined, 'bold');
        this.doc.text(title, this.margin, this.currentY);
        this.currentY += 8;

        const brandColorRgb = PDFUtils.hexToRgb(this.data.brandColor || '#003366');
        this.doc.setDrawColor(...brandColorRgb);
        this.doc.setLineWidth(0.5);
        this.doc.line(this.margin, this.currentY - 2, this.pageWidth - this.margin, this.currentY - 2);
        this.doc.setLineWidth(0.2);
        this.currentY += 3;
    }

    addField(label, value) {
        const brandColorRgb = PDFUtils.hexToRgb(this.data.brandColor || '#003366');
        const secondaryColorRgb = PDFUtils.hexToRgb(this.data.secondaryBrandColor || '#6c757d');

        this.doc.setFontSize(10);
        this.doc.setFont(undefined, 'bold');
        this.doc.setTextColor(...secondaryColorRgb);
        this.doc.text(`${label}:`, this.margin, this.currentY);
        this.currentY += 6;

        this.doc.setFont(undefined, 'normal');
        this.doc.setFontSize(9);

        if (Array.isArray(value) && value.length > 0) {
            this.addStyledList(value, brandColorRgb, secondaryColorRgb);
        } else {

            let fieldValue = this.formatFieldValue(value);

            if (fieldValue.trim().match(/^<h[1-6]/i)) {
                this.currentY += 3;
            }

            this.addFormattedText(fieldValue, this.margin + 5);
        }

        this.currentY += 3;
    }

    addFormattedText(html, xPos) {
        const maxWidth = this.pageWidth - (this.margin * 2) - 10;
        const baseFontSize = 9;
        const PT_TO_MM = 25.4 / 72;
        const lineHeightFactor = 1.15;
        const indentSize = 10;
        const bottomReserve = this.margin + this.footerHeight;

        const blocks = this.parseQuillHtml(html);

        this.doc.setTextColor(0, 0, 0);
        let currentX = xPos;
        let lineStartX = xPos;

        blocks.forEach((block, blockIndex) => {

            if (block.type === 'heading' && block.tag === 'h1' && blockIndex > 0) {
                this.currentY += 8;
            }

            let blockFontSize = baseFontSize;
            let isBoldForced = false;
            const blockIndent = (block.indent || 0) * indentSize;
            const bulletIndent = xPos + blockIndent;
            const textIndent = bulletIndent + 5;
            lineStartX = textIndent;
            currentX = textIndent;

            if (block.type === 'heading') {
                isBoldForced = true;
                switch (block.tag) {
                    case 'h1': blockFontSize = 18; break;
                    case 'h2': blockFontSize = 16; break;
                    case 'h3': blockFontSize = 14; break;
                    case 'h4': blockFontSize = 12; break;
                    case 'h5': blockFontSize = 11; break;
                    case 'h6': blockFontSize = 10; break;
                }
            }

            const lineHeight = blockFontSize * lineHeightFactor * PT_TO_MM;
            this.doc.setFontSize(blockFontSize);

            if (block.type === 'list-item') {
                const bulletText = block.ordered ? `${block.number}. ` : '• ';
                this.doc.setFont(undefined, 'bold');

                if (this.currentY + lineHeight > this.pageHeight - bottomReserve) {
                    this.addPageBreak();
                    lineStartX = textIndent;
                    currentX = textIndent;
                }

                this.doc.text(bulletText, bulletIndent, this.currentY);
                currentX = textIndent;
            }

            block.segments.forEach((segment) => {

                let fontStyle = 'normal';
                let bold = segment.bold || isBoldForced;
                let italic = segment.italic;
                if (bold && italic) {
                    fontStyle = 'bolditalic';
                } else if (bold) {
                    fontStyle = 'bold';
                } else if (italic) {
                    fontStyle = 'italic';
                }
                this.doc.setFont(undefined, fontStyle);

                if (segment.color) {
                    const rgb = PDFUtils.hexToRgb(segment.color);
                    this.doc.setTextColor(...rgb);
                } else {
                    this.doc.setTextColor(0, 0, 0);
                }

                const hasUnderline = segment.underline;

                const paragraphs = segment.text.split('\n');
                paragraphs.forEach((para, pidx) => {
                    const remainingWidth = maxWidth - (currentX - lineStartX);
                    const paraLines = this.doc.splitTextToSize(para, remainingWidth);

                    paraLines.forEach((line, lidx) => {
                        if (line.trim() === '') return;

                        if (this.currentY + lineHeight > this.pageHeight - bottomReserve) {
                            this.addPageBreak();
                            lineStartX = textIndent;
                            currentX = textIndent;
                        }

                        this.doc.text(line, currentX, this.currentY);

                        if (hasUnderline) {
                            const textWidth = this.doc.getTextWidth(line);
                            this.doc.setDrawColor(0, 0, 0);
                            this.doc.line(currentX, this.currentY + 1, currentX + textWidth, this.currentY + 1);
                        }

                        if (lidx < paraLines.length - 1) {
                            this.currentY += lineHeight;
                            currentX = lineStartX;
                        } else {
                            currentX += this.doc.getTextWidth(line);
                        }
                    });

                    if (pidx < paragraphs.length - 1) {
                        this.currentY += lineHeight;
                        currentX = lineStartX;
                    }
                });
            });

            this.currentY += lineHeight;
            currentX = xPos;
            lineStartX = xPos;

            if (block.type === 'heading') {
                this.currentY += lineHeight * 0.5;
            } else if (block.type === 'paragraph') {
                this.currentY += lineHeight * 0.3;
            }

        });

        this.currentY += 3;
        this.doc.setFontSize(baseFontSize);
    }

    parseQuillHtml(html) {
        html = String(html || '');
        if (!html.trim()) return [];

        const parser = new DOMParser();
        const dom = parser.parseFromString(html, 'text/html');
        const blocks = [];

        const processNode = (node) => {
            if (node.nodeType === 3) {
                return [{ text: node.textContent, bold: false, italic: false, underline: false, color: null }];
            }
            if (node.nodeType !== 1) return [];

            const tag = node.tagName.toLowerCase();
            if (tag === 'br') {
                return [{ text: '\n', bold: false, italic: false, underline: false, color: null }];
            }

            let bold = false, italic = false, underline = false, color = null;
            if (tag === 'strong' || tag === 'b') bold = true;
            if (tag === 'em' || tag === 'i') italic = true;
            if (tag === 'u') underline = true;
            if (tag === 'span') {
                const style = node.getAttribute('style');
                if (style) {
                    const colorMatch = style.match(/color:\s*([^;]+)/i);
                    if (colorMatch) color = colorMatch[1].trim();
                }
            }

            const segments = [];
            for (let child of node.childNodes) {
                const childSegments = processNode(child);
                childSegments.forEach(seg => {
                    if (bold) seg.bold = true;
                    if (italic) seg.italic = true;
                    if (underline) seg.underline = true;
                    if (color) seg.color = color;
                });
                segments.push(...childSegments);
            }
            return segments;
        };

        const processBlocks = (node, indent = 0) => {
            if (node.nodeType !== 1) return;

            const tag = node.tagName.toLowerCase();

            if (['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(tag)) {
                const segments = [];
                for (let child of node.childNodes) {
                    segments.push(...processNode(child));
                }

                if (tag.startsWith('h')) {
                    segments.forEach(seg => seg.bold = true);
                }
                blocks.push({
                    type: tag.startsWith('h') ? 'heading' : 'paragraph',
                    tag,
                    segments,
                    indent: 0
                });
            } else if (tag === 'ol' || tag === 'ul') {
                const ordered = tag === 'ol';
                let number = 1;
                for (let child of node.childNodes) {
                    if (child.nodeType === 1 && child.tagName.toLowerCase() === 'li') {
                        const segments = [];
                        for (let liChild of child.childNodes) {
                            if (liChild.nodeType === 1 && (liChild.tagName.toLowerCase() === 'ol' || liChild.tagName.toLowerCase() === 'ul')) {

                                processBlocks(liChild, indent + 1);
                            } else {
                                segments.push(...processNode(liChild));
                            }
                        }
                        blocks.push({
                            type: 'list-item',
                            segments,
                            ordered,
                            number: ordered ? number++ : 0,
                            indent
                        });
                    }
                }
            }
        };

        for (let child of dom.body.childNodes) {
            if (child.nodeType === 1) {

                processBlocks(child, 0);
            } else if (child.nodeType === 3 && child.textContent.trim()) {

                blocks.push({
                    type: 'paragraph',
                    tag: 'p',
                    segments: [{ text: child.textContent, bold: false, italic: false, underline: false, color: null }],
                    indent: 0
                });
            }
        }

        return blocks;
    }

    addStyledList(items, brandColorRgb, secondaryColorRgb) {
        const bulletIndent = this.margin + 5;
        const textIndent = bulletIndent + 5;
        const maxWidth = this.pageWidth - (this.margin * 2) - 15;
        const bottomReserve = this.margin + this.footerHeight;

        items.forEach((item) => {

            let itemText = '';
            if (typeof item === 'object') {
                const values = Object.values(item).filter(v => v && v.trim && v.trim().length > 0);
                itemText = values.join(' - ');
            } else {
                itemText = String(item);
            }

            const lines = this.doc.splitTextToSize(itemText, maxWidth);

            const itemHeight = Math.max(5, lines.length * this.lineHeight);

            if (this.currentY + itemHeight > this.pageHeight - bottomReserve) {
                this.addPageBreak();
            }

            this.doc.setTextColor(...brandColorRgb);
            this.doc.setFont(undefined, 'bold');
            this.doc.text('•', bulletIndent, this.currentY);

            this.doc.setTextColor(0, 0, 0);
            this.doc.setFont(undefined, 'normal');
            this.doc.text(lines, textIndent, this.currentY);

            this.currentY += itemHeight;
        });
    }

    formatFieldValue(value) {
        if (!value) return 'Not specified';

        if (Array.isArray(value)) {
            return this.formatArray(value);
        }

        if (typeof value === 'object') {
            return this.formatObject(value);
        }

        return String(value);
    }

    convertHtmlToText(html) {

        return html.replace(/<[^>]*>/g, '');
    }

    formatArray(arr) {
        if (arr.length === 0) return 'Not specified';

        return arr.map((item) => {
            if (typeof item === 'object') {

                const values = Object.values(item).filter(v => v && v.trim && v.trim().length > 0);
                return values.join(' - ');
            }
            return String(item);
        }).join('; ');
    }

    formatObject(obj) {
        const entries = Object.entries(obj);
        if (entries.length === 0) return 'Not specified';

        if (obj.weeklyHours !== undefined) {
            return `${obj.weeklyHours} hours/week, ${obj.shiftLength} hour shifts, ${obj.shiftsPerWeek} shifts/week`;
        }

        return entries
            .map(([key, value]) => {
                const formattedKey = key.replace(/([A-Z])/g, ' $1').trim();
                return `${formattedKey}: ${value}`;
            })
            .join('; ');
    }

    addDemandTable(title, data) {
        if (!data || Object.keys(data).length === 0) return;

        const demandType = this.getDemandTypeFromTitle(title);
        const headers = this.demandHeaders[demandType];
        if (!headers) return;

        this.checkPageBreak(30);

        this.doc.setFontSize(11);
        this.doc.setFont(undefined, 'bold');
        this.doc.text(title, this.margin, this.currentY);
        this.currentY += 6;

        const rows = this.buildDemandTableRows(data, headers);

        const pageContentWidth = this.pageWidth - (this.margin * 2);
        const numFrequencyColumns = headers.columns.length - 2;
        const frequencyColWidth = pageContentWidth * 0.12;
        const activityColWidth = pageContentWidth * 0.25;
        const commentsColWidth = pageContentWidth - activityColWidth - (frequencyColWidth * numFrequencyColumns);

        const columns = [
            { header: 'Activity', width: activityColWidth },
            ...headers.columns.map((col, index) => {
                let width;
                if (index === headers.columns.length - 1) {
                    width = commentsColWidth;
                } else {
                    width = frequencyColWidth;
                }
                return {
                    header: col,
                    width: width
                };
            })
        ];

        this.doc.autoTable({
            columns: columns,
            body: rows,
            startY: this.currentY,
            margin: {
                top: this.margin,
                right: this.margin,
                bottom: this.margin + this.footerHeight,
                left: this.margin
            },
            theme: 'grid',
            styles: {
                fontSize: 8,
                cellPadding: 2.5,
                overflow: 'linebreak',
                halign: 'center',
                valign: 'top'
            },
            headStyles: {
                fillColor: [41, 128, 185],
                textColor: 255,
                fontStyle: 'bold',
                halign: 'center',
                valign: 'middle'
            },
            bodyStyles: {
                halign: 'center',
                valign: 'top'
            },
            columnStyles: {
                0: { halign: 'left', valign: 'top' },
                [columns.length - 1]: { halign: 'left', valign: 'top' }
            },
            alternateRowStyles: {
                fillColor: [245, 245, 245]
            },
            didParseCell: (data) => {

                if (data.row.raw._isGroupHeader) {
                    data.cell.styles.fontStyle = 'bold';
                    data.cell.styles.fillColor = [200, 220, 240];
                    data.cell.styles.textColor = [0, 0, 0];
                    data.cell.styles.halign = 'left';
                }
            },
            didDrawPage: (data) => {

                const currentPageNum = this.doc.getNumberOfPages();
                if (currentPageNum > 1) {

                    data.settings.margin.top = this.margin + this.headerHeight + 3;
                }
                this.currentY = data.cursor.y + 3;
            }
        });

        this.currentY = this.doc.lastAutoTable.finalY + 15;
    }

    getDemandTypeFromTitle(title) {
        const titleMap = {
            'Physical Demands': 'physicalDemands',
            'Mobility Demands': 'mobilityDemands',
            'Cognitive/Sensory Demands': 'cognitiveSensoryDemands',
            'Environmental Demands': 'environmentalDemands',
            'Lifting/Pushing/Pulling': 'liftingPushingPulling'
        };
        return titleMap[title] || null;
    }

    buildDemandTableRows(data, headers) {
        const rows = [];

        if (headers.rowGroups && headers.rowGroups.length > 0) {
            headers.rowGroups.forEach((group) => {

                const groupHeaderRow = [group.category];

                for (let i = 1; i < headers.columns.length + 1; i++) {
                    groupHeaderRow.push('');
                }

                groupHeaderRow._isGroupHeader = true;
                rows.push(groupHeaderRow);

                group.rows.forEach((rowIndex) => {
                    const rowLabel = headers.rows[rowIndex];
                    const row = [rowLabel];

                    for (let colIndex = 0; colIndex < headers.columns.length; colIndex++) {
                        const cellValue = data[rowIndex]?.[colIndex];

                        if (typeof cellValue === 'boolean') {
                            row.push(cellValue ? 'X' : '');
                        } else if (typeof cellValue === 'string') {
                            row.push(cellValue);
                        } else {
                            row.push('');
                        }
                    }

                    rows.push(row);
                });
            });
        } else {

            headers.rows.forEach((rowLabel, rowIndex) => {
                const row = [rowLabel];

                for (let colIndex = 0; colIndex < headers.columns.length; colIndex++) {
                    const cellValue = data[rowIndex]?.[colIndex];

                    if (typeof cellValue === 'boolean') {
                        row.push(cellValue ? 'X' : '');
                    } else if (typeof cellValue === 'string') {
                        row.push(cellValue);
                    } else {
                        row.push('');
                    }
                }

                rows.push(row);
            });
        }

        return rows;
    }

    checkPageBreak(spaceNeeded) {

        const bottomReserve = this.margin + this.footerHeight;
        if (this.currentY + spaceNeeded > this.pageHeight - bottomReserve) {
            this.addPageBreak();
        }
    }

    addPageBreak() {
        this.doc.addPage();

        const currentPageNum = this.doc.getNumberOfPages();
        if (currentPageNum > 1) {

            this.currentY = this.margin + this.headerHeight + 3;
        } else {
            this.currentY = this.margin;
        }
    }
}

window.Template1Generator = Template1Generator;