import { getCell, renderCell, isNonEmptyCell, encodeCellAddress } from '../inspect/xlsx-cells.js';
import { estimateTokens } from '../inspect/shared.js';
import { wrapDependencyLoadError } from '../utils.js';
// Lazy-loaded so importing this module never pulls in xlsx.
// CJS interop: prefer .default (full API object) and fall back to the namespace.
let xlsxModule;
async function loadXlsx() {
    xlsxModule ??= import('xlsx')
        .then(mod => mod.default ?? mod)
        .catch((error) => wrapDependencyLoadError('xlsx', error));
    return xlsxModule;
}
function buildMergeMap(sheet) {
    const map = new Map();
    const merges = sheet['!merges'] ?? [];
    for (const merge of merges) {
        const key = encodeCellAddress(merge.s.r, merge.s.c);
        map.set(key, {
            colSpan: merge.e.c - merge.s.c + 1,
            rowSpan: merge.e.r - merge.s.r + 1,
        });
    }
    return map;
}
function buildMergedCellSet(sheet) {
    const set = new Set();
    const merges = sheet['!merges'] ?? [];
    for (const merge of merges) {
        for (let r = merge.s.r; r <= merge.e.r; r++) {
            for (let c = merge.s.c; c <= merge.e.c; c++) {
                if (r === merge.s.r && c === merge.s.c)
                    continue;
                set.add(encodeCellAddress(r, c));
            }
        }
    }
    return set;
}
function detectHeaders(sheet, startRow, startCol, endCol, headerRow) {
    if (headerRow === 'none')
        return null;
    const rowIndex = typeof headerRow === 'number' ? headerRow - 1 : startRow;
    const values = [];
    let nonEmpty = 0;
    for (let c = startCol; c <= endCol; c++) {
        const cell = getCell(sheet, rowIndex, c);
        const val = renderCell(cell);
        values.push(val);
        if (val)
            nonEmpty++;
    }
    if (nonEmpty === 0)
        return null;
    if (headerRow === 'auto') {
        // Check if all non-empty values are unique strings
        const nonEmptyValues = values.filter(v => v.length > 0);
        if (nonEmptyValues.length > 0 && new Set(nonEmptyValues).size === nonEmptyValues.length) {
            // Check that values look like headers (all strings in the row)
            let allString = true;
            for (let c = startCol; c <= endCol; c++) {
                const cell = getCell(sheet, rowIndex, c);
                if (cell && isNonEmptyCell(cell) && cell.t !== 's' && cell.t !== 'z') {
                    allString = false;
                    break;
                }
            }
            if (allString)
                return values;
        }
        return null;
    }
    return values;
}
export async function extractXlsxTables(buffer, options) {
    const XLSX = await loadXlsx();
    const workbook = XLSX.read(buffer, {
        type: 'buffer',
        cellText: true,
        cellDates: true,
        sheetStubs: true,
    });
    const tables = [];
    let tableIndex = 0;
    for (let si = 0; si < workbook.SheetNames.length; si++) {
        const sheetName = workbook.SheetNames[si];
        const sheet = workbook.Sheets[sheetName];
        if (!sheet || !sheet['!ref'])
            continue;
        tableIndex++;
        const range = XLSX.utils.decode_range(sheet['!ref']);
        const mergeMap = buildMergeMap(sheet);
        const mergedCellSet = buildMergedCellSet(sheet);
        const startRow = range.s.r;
        const startCol = range.s.c;
        const endCol = range.e.c;
        const endRow = range.e.r;
        // Detect headers
        const headers = detectHeaders(sheet, startRow, startCol, endCol, options.headerRow);
        const dataStartRow = headers ? startRow + 1 : startRow;
        const totalRows = endRow - startRow + 1;
        const totalCols = endCol - startCol + 1;
        // Build rows
        const maxRows = options.sampleRows;
        const dataRowCount = endRow - dataStartRow + 1;
        const truncated = dataRowCount > maxRows;
        const rowLimit = Math.min(dataRowCount, maxRows);
        const rows = [];
        let chars = 0;
        // Count total chars for token estimate (including header row)
        if (headers) {
            for (const h of headers)
                chars += h.length + 1;
            chars += 1;
        }
        let nonEmptyRowCount = 0;
        for (let r = dataStartRow; r <= endRow; r++) {
            const cells = [];
            let rowHasContent = false;
            for (let c = startCol; c <= endCol; c++) {
                const cellAddr = encodeCellAddress(r, c);
                if (mergedCellSet.has(cellAddr)) {
                    cells.push({ value: '' });
                    continue;
                }
                const cell = getCell(sheet, r, c);
                const value = renderCell(cell);
                const tableCell = { value };
                const merge = mergeMap.get(cellAddr);
                if (merge) {
                    if (merge.colSpan > 1)
                        tableCell.colSpan = merge.colSpan;
                    if (merge.rowSpan > 1)
                        tableCell.rowSpan = merge.rowSpan;
                }
                if (value)
                    rowHasContent = true;
                cells.push(tableCell);
                chars += value.length + 1;
            }
            chars += 1;
            if (!rowHasContent)
                continue;
            nonEmptyRowCount++;
            if (rows.length < rowLimit) {
                rows.push({ index: r - startRow + 1, cells });
            }
        }
        // Cell count
        let cellCount = 0;
        for (let r = startRow; r <= endRow; r++) {
            for (let c = startCol; c <= endCol; c++) {
                const cell = getCell(sheet, r, c);
                if (cell && isNonEmptyCell(cell))
                    cellCount++;
            }
        }
        tables.push({
            tableIndex,
            location: `Sheet: ${sheetName}`,
            rowCount: totalRows,
            columnCount: totalCols,
            cellCount,
            headers,
            rows,
            truncated: truncated && rows.length >= rowLimit,
            tokenEstimate: estimateTokens(chars),
        });
    }
    return tables;
}
//# sourceMappingURL=inspect-xlsx.js.map