"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.DocumentExportTool = void 0;
const fs = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
const pathResolution_1 = require("./pathResolution");
const ALLOWED_FORMATS = ['csv', 'xlsx', 'docx', 'pdf'];
/**
 * Generates documents in CSV, Excel (.xlsx), Word (.docx), or PDF format.
 * Files are written to the project directory or configured external paths.
 */
exports.DocumentExportTool = {
    name: 'document_export',
    description: 'Generate a CSV, Excel (.xlsx), Word (.docx), or PDF file. For CSV: provide rows as an array of arrays. For Excel: provide sheets with rows. For Word/PDF: provide a body array of paragraph and table elements. Paragraph: { type: "paragraph", text, heading?: 1|2|3, bold?, italic? }. Table: { type: "table", headers: string[], rows: string[][] }.',
    parameters: {
        type: 'object',
        properties: {
            path: { type: 'string', description: 'Output file path (e.g. "report.xlsx", "data.csv", "brief.docx", "summary.pdf")' },
            format: { type: 'string', description: 'Output format: csv, xlsx, docx, or pdf', enum: ['csv', 'xlsx', 'docx', 'pdf'] },
            title: { type: 'string', description: 'Document title (used in docx/pdf headers)' },
            content: {
                type: 'object',
                description: 'Document content. CSV: { rows: string[][] }. Excel: { sheets: [{ name, rows }] } or { rows }. Word/PDF: { markdown: string } or { body: [{ type: "paragraph", text, heading?, bold?, italic? }, { type: "table", headers: string[], rows: string[][] }] }. Legacy "paragraphs" array still works for backward compatibility.',
            },
        },
        required: ['path', 'format', 'content'],
    },
    isReadOnly: false,
    async execute(input) {
        const rawPath = String(input.path ?? '');
        const format = String(input.format ?? '');
        if (!ALLOWED_FORMATS.includes(format)) {
            return { success: false, output: `Unsupported format "${format}". Use: ${ALLOWED_FORMATS.join(', ')}`, error: 'bad format' };
        }
        // Use the same redirect chain as file_write so documents land in the
        // configured Agent Files directory (C:\AI\Oracle) instead of the project root.
        let filePath = (0, pathResolution_1.applyFileWriteRedirect)(rawPath);
        let redirectNote = filePath ? ' (redirected by pattern rule)' : '';
        if (!filePath) {
            const bareRedirect = (0, pathResolution_1.maybeRedirectAgentOutput)(rawPath);
            if (bareRedirect) {
                filePath = bareRedirect;
                redirectNote = ' (redirected to Agent Files)';
            }
            else {
                filePath = (0, pathResolution_1.resolveProjectPath)(rawPath);
            }
        }
        if (!filePath) {
            return { success: false, output: 'Path is outside the project directory', error: 'path outside project' };
        }
        const title = String(input.title ?? 'Document');
        const content = input.content;
        if (!content) {
            return { success: false, output: 'Content is required', error: 'missing content' };
        }
        try {
            await fs.mkdir(path.dirname(filePath), { recursive: true });
            switch (format) {
                case 'csv':
                    await writeCsv(filePath, content);
                    break;
                case 'xlsx':
                    await writeXlsx(filePath, title, content);
                    break;
                case 'docx':
                    await writeDocx(filePath, title, content);
                    break;
                case 'pdf':
                    await writePdf(filePath, title, content);
                    break;
            }
            const stat = await fs.stat(filePath);
            return { success: true, output: `Wrote ${format.toUpperCase()} document (${stat.size} bytes) to '${filePath}'${redirectNote}` };
        }
        catch (error) {
            const msg = error instanceof Error ? error.message : String(error);
            return { success: false, output: `Failed to write ${format}: ${msg}`, error: msg };
        }
    },
};
// ─── CSV ────────────────────────────────────────────────────────────
function escapeCsvField(value) {
    const s = String(value ?? '');
    if (s.includes(',') || s.includes('"') || s.includes('\n') || s.includes('\r')) {
        return '"' + s.replace(/"/g, '""') + '"';
    }
    return s;
}
async function writeCsv(filePath, content) {
    const rows = Array.isArray(content.rows) ? content.rows : [];
    if (rows.length === 0)
        throw new Error('CSV content must have a "rows" array');
    const csv = rows.map((row) => {
        const cells = Array.isArray(row) ? row : [row];
        return cells.map(escapeCsvField).join(',');
    }).join('\n') + '\n';
    await fs.writeFile(filePath, csv, 'utf-8');
}
// ─── Excel (.xlsx) ──────────────────────────────────────────────────
function coerceExcelValue(raw) {
    if (typeof raw === 'number')
        return raw;
    if (raw === null || raw === undefined)
        return '';
    const s = String(raw).trim();
    // Percentage: "61%" → 0.61 (Excel native percentage)
    const pctMatch = s.match(/^(-?\d+(?:\.\d+)?)%$/);
    if (pctMatch)
        return parseFloat(pctMatch[1]) / 100;
    // Currency: "£4.50", "$18.00" → 4.50 (strip symbol, keep number)
    const curMatch = s.match(/^[£$€¥]?\s*(-?\d{1,3}(?:,\d{3})*(?:\.\d+)?)$/);
    if (curMatch)
        return parseFloat(curMatch[1].replace(/,/g, ''));
    // Plain number: "12.80" → 12.8
    const num = Number(s);
    if (s !== '' && !isNaN(num) && isFinite(num))
        return num;
    return s;
}
async function writeXlsx(filePath, title, content) {
    const ExcelJS = await Promise.resolve().then(() => __importStar(require('exceljs')));
    const workbook = new ExcelJS.Workbook();
    workbook.creator = 'Harness';
    workbook.created = new Date();
    const sheets = Array.isArray(content.sheets) ? content.sheets : [];
    if (sheets.length === 0) {
        const rows = Array.isArray(content.rows) ? content.rows : [];
        if (rows.length === 0)
            throw new Error('Excel content must have "sheets" or "rows" array');
        const ws = workbook.addWorksheet(title);
        for (let i = 0; i < rows.length; i++) {
            const row = Array.isArray(rows[i]) ? rows[i] : [rows[i]];
            ws.addRow(i === 0 ? row.map((v) => String(v ?? '')) : row.map(coerceExcelValue));
        }
        styleHeaderRow(ws);
    }
    else {
        for (const sheet of sheets) {
            const name = String(sheet.name ?? 'Sheet');
            const rows = Array.isArray(sheet.rows) ? sheet.rows : [];
            const ws = workbook.addWorksheet(name);
            for (let i = 0; i < rows.length; i++) {
                const row = Array.isArray(rows[i]) ? rows[i] : [rows[i]];
                ws.addRow(i === 0 ? row.map((v) => String(v ?? '')) : row.map(coerceExcelValue));
            }
            styleHeaderRow(ws);
        }
    }
    await workbook.xlsx.writeFile(filePath);
}
function styleHeaderRow(ws) {
    const header = ws.getRow(1);
    header.font = { bold: true };
    header.eachCell((cell) => {
        cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE2E8F0' } };
    });
    ws.columns.forEach((col) => {
        col.width = Math.max(12, Math.min(40, (col.header?.toString().length ?? 10) + 4));
    });
}
function parseBody(content) {
    if (typeof content.markdown === 'string')
        return markdownToBody(content.markdown);
    // Prefer "body" array (new format with mixed paragraphs and tables).
    if (Array.isArray(content.body))
        return content.body;
    // Fall back to legacy "paragraphs" array.
    if (Array.isArray(content.paragraphs)) {
        return content.paragraphs.map((p) => ({ ...p, type: 'paragraph' }));
    }
    return [];
}
function markdownToBody(markdown) {
    const body = [];
    const paragraphLines = [];
    const lines = markdown.replace(/\r\n/g, '\n').split('\n');
    const flushParagraph = () => {
        const text = paragraphLines.join(' ').replace(/\s+/g, ' ').trim();
        paragraphLines.length = 0;
        if (text)
            body.push({ type: 'paragraph', text });
    };
    const parseTableRow = (line) => line.trim().replace(/^\|/, '').replace(/\|$/, '').split('|').map((cell) => cell.trim());
    const isTableSeparator = (line) => parseTableRow(line).every((cell) => /^:?-{3,}:?$/.test(cell));
    for (let i = 0; i < lines.length; i++) {
        const rawLine = lines[i];
        const line = rawLine.trim();
        if (!line) {
            flushParagraph();
            continue;
        }
        const heading = line.match(/^(#{1,3})\s+(.+)$/);
        if (heading) {
            flushParagraph();
            body.push({ type: 'paragraph', heading: heading[1].length, text: cleanMarkdownText(heading[2]) });
            continue;
        }
        if (line.includes('|') && i + 1 < lines.length && isTableSeparator(lines[i + 1].trim())) {
            flushParagraph();
            const headers = parseTableRow(line).map(cleanMarkdownText);
            i += 2;
            const rows = [];
            while (i < lines.length && lines[i].trim().includes('|')) {
                rows.push(parseTableRow(lines[i].trim()).map(cleanMarkdownText));
                i += 1;
            }
            i -= 1;
            body.push({ type: 'table', headers, rows });
            continue;
        }
        const listItem = line.match(/^[-*]\s+(.+)$/) || line.match(/^\d+[.)]\s+(.+)$/);
        if (listItem) {
            flushParagraph();
            body.push({ type: 'paragraph', text: `- ${cleanMarkdownText(listItem[1])}` });
            continue;
        }
        paragraphLines.push(cleanMarkdownText(line));
    }
    flushParagraph();
    return body;
}
function cleanMarkdownText(value) {
    return value
        .replace(/`([^`]+)`/g, '$1')
        .replace(/\*\*([^*]+)\*\*/g, '$1')
        .replace(/__([^_]+)__/g, '$1')
        .replace(/\*([^*]+)\*/g, '$1')
        .replace(/_([^_]+)_/g, '$1')
        .trim();
}
async function writeDocx(filePath, title, content) {
    const docx = await Promise.resolve().then(() => __importStar(require('docx')));
    const body = parseBody(content);
    if (body.length === 0)
        throw new Error('Word content must have a "body" or "paragraphs" array');
    const children = [];
    // Title
    children.push(new docx.Paragraph({
        children: [new docx.TextRun({ text: title, bold: true, size: 36 })],
        heading: docx.HeadingLevel.TITLE,
        spacing: { after: 200 },
    }));
    for (const element of body) {
        if (element.type === 'table') {
            const table = element;
            const tableRows = [];
            if (table.headers && table.headers.length > 0) {
                tableRows.push(new docx.TableRow({
                    tableHeader: true,
                    children: table.headers.map((h) => new docx.TableCell({
                        children: [new docx.Paragraph({ children: [new docx.TextRun({ text: String(h), bold: true })] })],
                        shading: { type: docx.ShadingType.SOLID, color: 'E2E8F0' },
                    })),
                }));
            }
            for (const row of table.rows ?? []) {
                const cells = Array.isArray(row) ? row : [row];
                tableRows.push(new docx.TableRow({
                    children: cells.map((c) => new docx.TableCell({
                        children: [new docx.Paragraph({ children: [new docx.TextRun({ text: String(c ?? '') })] })],
                    })),
                }));
            }
            if (tableRows.length > 0) {
                children.push(new docx.Table({ rows: tableRows }));
                children.push(new docx.Paragraph({ text: '', spacing: { after: 120 } }));
            }
        }
        else {
            const para = element;
            const text = String(para.text ?? '');
            if (!text)
                continue;
            const heading = typeof para.heading === 'number' && para.heading >= 1 && para.heading <= 3
                ? [docx.HeadingLevel.HEADING_1, docx.HeadingLevel.HEADING_2, docx.HeadingLevel.HEADING_3][para.heading - 1]
                : undefined;
            children.push(new docx.Paragraph({
                children: [new docx.TextRun({ text, bold: para.bold, italics: para.italic })],
                heading,
                spacing: { after: 120 },
            }));
        }
    }
    const doc = new docx.Document({
        creator: 'Harness',
        title,
        sections: [{ children }],
    });
    const buffer = await docx.Packer.toBuffer(doc);
    await fs.writeFile(filePath, buffer);
}
// ─── PDF ────────────────────────────────────────────────────────────
/** Strip characters outside the Latin-1 range that Helvetica cannot render. */
function stripUnsupportedChars(text) {
    // Remove emoji and other characters above U+00FF, replacing with a space so words don't merge.
    return text.replace(/[^\u0000-\u00FF]+/g, ' ').replace(/  +/g, ' ').trim();
}
async function writePdf(filePath, title, content) {
    const PDFDocument = (await Promise.resolve().then(() => __importStar(require('pdfkit')))).default;
    const body = parseBody(content);
    if (body.length === 0)
        throw new Error('PDF content must have a "body" or "paragraphs" array');
    const LEFT_MARGIN = 60;
    const RIGHT_MARGIN = 60;
    const TOP_MARGIN = 60;
    const BOTTOM_MARGIN = 60;
    return new Promise((resolve, reject) => {
        const doc = new PDFDocument({
            size: 'A4',
            bufferPages: true,
            margins: { top: TOP_MARGIN, bottom: BOTTOM_MARGIN, left: LEFT_MARGIN, right: RIGHT_MARGIN },
            info: { Title: stripUnsupportedChars(title), Author: 'Harness' },
        });
        const chunks = [];
        let totalPdfBytes = 0;
        const MAX_PDF_BYTES = 10 * 1024 * 1024; // 10 MB
        doc.on('data', (chunk) => {
            totalPdfBytes += chunk.length;
            if (totalPdfBytes > MAX_PDF_BYTES) {
                doc.end();
                reject(new Error(`PDF generation aborted: output exceeded ${MAX_PDF_BYTES} byte limit`));
                return;
            }
            chunks.push(chunk);
        });
        doc.on('end', async () => {
            try {
                await fs.writeFile(filePath, Buffer.concat(chunks));
                resolve();
            }
            catch (err) {
                reject(err);
            }
        });
        doc.on('error', reject);
        const pageWidth = doc.page.width - LEFT_MARGIN - RIGHT_MARGIN;
        // ── Title block ──────────────────────────────────────────────────
        doc.fillColor('#1E293B').fontSize(22).font('Helvetica-Bold')
            .text(stripUnsupportedChars(title), LEFT_MARGIN, TOP_MARGIN, { align: 'center', width: pageWidth });
        // Rule under title
        const ruleY = doc.y + 6;
        doc.moveTo(LEFT_MARGIN, ruleY).lineTo(LEFT_MARGIN + pageWidth, ruleY).strokeColor('#94A3B8').lineWidth(0.75).stroke();
        doc.fillColor('#000000');
        doc.moveDown(1.2);
        const resetX = () => { doc.x = LEFT_MARGIN; };
        for (const element of body) {
            if (element.type === 'table') {
                const table = element;
                const allRows = [...(table.headers ? [table.headers] : []), ...(table.rows ?? [])];
                if (allRows.length === 0)
                    continue;
                const colCount = Math.max(...allRows.map((r) => (Array.isArray(r) ? r.length : 1)));
                // Two-column tables: give the first column 30% so content-heavy second columns breathe.
                const colWidths = colCount === 2
                    ? [pageWidth * 0.30, pageWidth * 0.70]
                    : Array(colCount).fill(pageWidth / colCount);
                const cellPadding = 5;
                for (let rowIdx = 0; rowIdx < allRows.length; rowIdx++) {
                    const row = allRows[rowIdx];
                    const cells = Array.isArray(row) ? row : [row];
                    const isHeader = rowIdx === 0 && table.headers && table.headers.length > 0;
                    const isAltRow = !isHeader && rowIdx % 2 === 0;
                    const fontName = isHeader ? 'Helvetica-Bold' : 'Helvetica';
                    const fontSize = isHeader ? 9 : 9;
                    // Measure required row height across all cells.
                    let maxCellHeight = isHeader ? 18 : 14;
                    const cellTexts = [];
                    for (let colIdx = 0; colIdx < colCount; colIdx++) {
                        const raw = String(cells[colIdx] ?? '');
                        const cellText = stripUnsupportedChars(raw);
                        cellTexts.push(cellText);
                        const textHeight = doc.font(fontName).fontSize(fontSize)
                            .heightOfString(cellText, { width: colWidths[colIdx] - cellPadding * 2 });
                        maxCellHeight = Math.max(maxCellHeight, textHeight + cellPadding * 2);
                    }
                    const rowHeight = Math.ceil(maxCellHeight);
                    const bottomMargin = doc.page.margins.bottom ?? BOTTOM_MARGIN;
                    if (doc.y + rowHeight > doc.page.height - bottomMargin) {
                        doc.addPage();
                    }
                    const y = doc.y;
                    let xCursor = LEFT_MARGIN;
                    for (let colIdx = 0; colIdx < colCount; colIdx++) {
                        const cw = colWidths[colIdx];
                        doc.save();
                        if (isHeader) {
                            doc.rect(xCursor, y, cw, rowHeight).fill('#334155').stroke('#1E293B');
                            doc.fillColor('#FFFFFF').fontSize(fontSize).font('Helvetica-Bold');
                        }
                        else if (isAltRow) {
                            doc.rect(xCursor, y, cw, rowHeight).fill('#F8FAFC').stroke('#E2E8F0');
                            doc.fillColor('#000000').fontSize(fontSize).font('Helvetica');
                        }
                        else {
                            doc.rect(xCursor, y, cw, rowHeight).fill('#FFFFFF').stroke('#E2E8F0');
                            doc.fillColor('#000000').fontSize(fontSize).font('Helvetica');
                        }
                        doc.text(cellTexts[colIdx], xCursor + cellPadding, y + cellPadding, { width: cw - cellPadding * 2, lineGap: 1.5 });
                        doc.restore();
                        xCursor += cw;
                    }
                    doc.y = y + rowHeight;
                }
                // Reset cursor to left margin and add spacing after table.
                resetX();
                doc.moveDown(0.8);
            }
            else {
                const para = element;
                const text = stripUnsupportedChars(String(para.text ?? ''));
                if (!text)
                    continue;
                resetX();
                const bottomMargin = doc.page.margins.bottom ?? BOTTOM_MARGIN;
                if (para.heading === 1) {
                    if (doc.y > doc.page.height - bottomMargin - 80)
                        doc.addPage();
                    doc.moveDown(0.8);
                    // Heading 1: dark background band
                    const hY = doc.y;
                    doc.rect(LEFT_MARGIN, hY, pageWidth, 26).fill('#1E293B');
                    doc.fillColor('#FFFFFF').fontSize(15).font('Helvetica-Bold')
                        .text(text, LEFT_MARGIN + 8, hY + 6, { width: pageWidth - 16 });
                    doc.fillColor('#000000');
                    doc.moveDown(0.6);
                }
                else if (para.heading === 2) {
                    if (doc.y > doc.page.height - bottomMargin - 60)
                        doc.addPage();
                    doc.moveDown(0.6);
                    doc.fillColor('#1E40AF').fontSize(13).font('Helvetica-Bold').text(text);
                    // Underline
                    const ulY = doc.y + 1;
                    doc.moveTo(LEFT_MARGIN, ulY).lineTo(LEFT_MARGIN + pageWidth * 0.4, ulY)
                        .strokeColor('#3B82F6').lineWidth(0.5).stroke();
                    doc.fillColor('#000000');
                    doc.moveDown(0.4);
                }
                else if (para.heading === 3) {
                    if (doc.y > doc.page.height - bottomMargin - 40)
                        doc.addPage();
                    doc.moveDown(0.4);
                    doc.fillColor('#374151').fontSize(11).font('Helvetica-Bold').text(text);
                    doc.fillColor('#000000');
                    doc.moveDown(0.3);
                }
                else if (para.bold) {
                    doc.fontSize(11).font('Helvetica-Bold').text(text, { lineGap: 2 });
                    doc.moveDown(0.3);
                }
                else if (para.italic) {
                    doc.fontSize(10).font('Helvetica-Oblique').fillColor('#374151').text(text, { lineGap: 2 });
                    doc.fillColor('#000000');
                    doc.moveDown(0.3);
                }
                else if (text.startsWith('- ')) {
                    // List item: indent + bullet
                    const bulletText = '\u2022  ' + text.slice(2);
                    doc.fontSize(10.5).font('Helvetica').text(bulletText, LEFT_MARGIN + 12, doc.y, { width: pageWidth - 12, lineGap: 2 });
                    doc.moveDown(0.2);
                }
                else {
                    doc.fontSize(10.5).font('Helvetica').text(text, LEFT_MARGIN, doc.y, { width: pageWidth, lineGap: 3, paragraphGap: 2 });
                    doc.moveDown(0.35);
                }
            }
        }
        // Footer: page numbers
        const range = doc.bufferedPageRange();
        for (let i = 0; i < range.count; i++) {
            doc.switchToPage(range.start + i);
            doc.fontSize(8).fillColor('#94A3B8').font('Helvetica')
                .text(`Page ${i + 1} of ${range.count}`, LEFT_MARGIN, doc.page.height - BOTTOM_MARGIN + 20, { width: pageWidth, align: 'center' });
        }
        doc.end();
    });
}
//# sourceMappingURL=documentTools.js.map