import { formatAddress, indexToColumn } from '@bilig/formula';
import { addEngineCounter } from '../perf/engine-counters.js';
const INVALIDATION_CELL_PATCH_LIMIT = 512;
function readChangedCell(args, cellIndex, fallbackSheetName) {
    const sheetId = args.workbook.cellStore.sheetIds[cellIndex];
    if (sheetId === undefined) {
        return null;
    }
    const sheet = args.workbook.getSheetById(sheetId);
    const position = sheet?.structureVersion === 1
        ? {
            row: args.workbook.cellStore.rows[cellIndex],
            col: args.workbook.cellStore.cols[cellIndex],
        }
        : args.workbook.getCellPosition(cellIndex);
    const row = position?.row;
    const col = position?.col;
    /* v8 ignore next -- defensive guard for stale logical indices without visible positions. */
    if (row === undefined || col === undefined) {
        return null;
    }
    return {
        kind: 'cell',
        cellIndex,
        address: { sheet: sheetId, row, col },
        sheetName: fallbackSheetName ?? args.workbook.getSheetNameById(sheetId),
        a1: formatAddress(row, col),
        newValue: args.workbook.cellStore.getValue(cellIndex, (id) => args.strings.get(id)),
    };
}
function materializeChangedCellPatches(args, changedCellIndices) {
    if (changedCellIndices.length === 0) {
        return [];
    }
    if (changedCellIndices.length <= 2) {
        const first = readChangedCell(args, changedCellIndices[0]);
        if (!first) {
            return [];
        }
        if (changedCellIndices.length === 1) {
            if (args.counters) {
                addEngineCounter(args.counters, 'changedCellPayloadsBuilt', 1);
            }
            return [first];
        }
        const secondCellIndex = changedCellIndices[1];
        const secondSheetId = args.workbook.cellStore.sheetIds[secondCellIndex];
        const second = secondSheetId !== undefined && secondSheetId === first.address.sheet
            ? readChangedCell(args, secondCellIndex, first.sheetName)
            : readChangedCell(args, secondCellIndex);
        const patches = second ? [first, second] : [first];
        if (args.counters) {
            addEngineCounter(args.counters, 'changedCellPayloadsBuilt', patches.length);
        }
        return patches;
    }
    let firstSheetId;
    for (let index = 0; index < changedCellIndices.length; index += 1) {
        const sheetId = args.workbook.cellStore.sheetIds[changedCellIndices[index]];
        if (sheetId !== undefined) {
            firstSheetId = sheetId;
            break;
        }
    }
    if (firstSheetId === undefined) {
        return [];
    }
    let isSingleSheetChangeSet = true;
    for (let index = 0; index < changedCellIndices.length; index += 1) {
        const sheetId = args.workbook.cellStore.sheetIds[changedCellIndices[index]];
        if (sheetId !== undefined && sheetId !== firstSheetId) {
            isSingleSheetChangeSet = false;
            break;
        }
    }
    if (isSingleSheetChangeSet) {
        const sheet = args.workbook.getSheetById(firstSheetId);
        const sheetName = sheet?.name ?? args.workbook.getSheetNameById(firstSheetId);
        const isPhysicalSheet = !sheet || sheet.structureVersion === 1;
        const columnLabels = [];
        const formatAddressCached = (row, col) => {
            let label = columnLabels[col];
            if (label === undefined) {
                label = indexToColumn(col);
                columnLabels[col] = label;
            }
            return `${label}${row + 1}`;
        };
        const patches = [];
        for (let index = 0; index < changedCellIndices.length; index += 1) {
            const cellIndex = changedCellIndices[index];
            if (args.workbook.cellStore.sheetIds[cellIndex] !== firstSheetId) {
                continue;
            }
            let row;
            let col;
            if (isPhysicalSheet) {
                row = args.workbook.cellStore.rows[cellIndex];
                col = args.workbook.cellStore.cols[cellIndex];
            }
            else {
                const position = args.workbook.getCellPosition(cellIndex);
                /* v8 ignore next -- defensive guard for stale logical indices without visible positions. */
                if (!position) {
                    continue;
                }
                row = position.row;
                col = position.col;
            }
            patches.push({
                kind: 'cell',
                cellIndex,
                address: { sheet: firstSheetId, row, col },
                sheetName,
                a1: formatAddressCached(row, col),
                newValue: args.workbook.cellStore.getValue(cellIndex, (id) => args.strings.get(id)),
            });
        }
        if (args.counters) {
            addEngineCounter(args.counters, 'changedCellPayloadsBuilt', patches.length);
        }
        return patches;
    }
    const sheetNames = new Map();
    const physicalSheetIds = new Set();
    const logicalSheetIds = new Set();
    const columnLabels = [];
    const formatAddressCached = (row, col) => {
        let label = columnLabels[col];
        if (label === undefined) {
            label = indexToColumn(col);
            columnLabels[col] = label;
        }
        return `${label}${row + 1}`;
    };
    const patches = [];
    for (let index = 0; index < changedCellIndices.length; index += 1) {
        const cellIndex = changedCellIndices[index];
        const sheetId = args.workbook.cellStore.sheetIds[cellIndex];
        if (sheetId === undefined) {
            continue;
        }
        let sheetName = sheetNames.get(sheetId);
        if (sheetName === undefined) {
            const sheet = args.workbook.getSheetById(sheetId);
            sheetName = sheet?.name ?? args.workbook.getSheetNameById(sheetId);
            sheetNames.set(sheetId, sheetName);
            if (!sheet || sheet.structureVersion === 1) {
                physicalSheetIds.add(sheetId);
            }
            else {
                logicalSheetIds.add(sheetId);
            }
        }
        let row;
        let col;
        if (physicalSheetIds.has(sheetId)) {
            row = args.workbook.cellStore.rows[cellIndex];
            col = args.workbook.cellStore.cols[cellIndex];
        }
        else {
            const position = args.workbook.getCellPosition(cellIndex);
            /* v8 ignore next -- defensive guard for stale logical indices without visible positions. */
            if (!position) {
                continue;
            }
            row = position.row;
            col = position.col;
        }
        patches.push({
            kind: 'cell',
            cellIndex,
            address: { sheet: sheetId, row, col },
            sheetName,
            a1: formatAddressCached(row, col),
            newValue: args.workbook.cellStore.getValue(cellIndex, (id) => args.strings.get(id)),
        });
    }
    if (args.counters) {
        addEngineCounter(args.counters, 'changedCellPayloadsBuilt', patches.length);
    }
    return patches;
}
function materializeRangeInvalidationPatches(invalidatedRanges) {
    return invalidatedRanges.map((range) => ({
        kind: 'range-invalidation',
        range,
    }));
}
function materializeRowInvalidationPatches(invalidatedRows) {
    return (invalidatedRows ?? []).map((row) => ({
        kind: 'row-invalidation',
        sheetName: row.sheetName,
        startIndex: row.startIndex,
        endIndex: row.endIndex,
    }));
}
function materializeColumnInvalidationPatches(invalidatedColumns) {
    return (invalidatedColumns ?? []).map((column) => ({
        kind: 'column-invalidation',
        sheetName: column.sheetName,
        startIndex: column.startIndex,
        endIndex: column.endIndex,
    }));
}
export function materializeEnginePatches(args, request) {
    const rangePatches = materializeRangeInvalidationPatches(request.invalidatedRanges ?? []);
    const rowPatches = materializeRowInvalidationPatches(request.invalidatedRows);
    const columnPatches = materializeColumnInvalidationPatches(request.invalidatedColumns);
    if (rangePatches.length === 0 && rowPatches.length === 0 && columnPatches.length === 0) {
        return materializeChangedCellPatches(args, request.changedCellIndices);
    }
    if (request.invalidation === 'full' || rowPatches.length > 0 || request.changedCellIndices.length > INVALIDATION_CELL_PATCH_LIMIT) {
        return [...rangePatches, ...rowPatches, ...columnPatches];
    }
    const cellPatches = materializeChangedCellPatches(args, request.changedCellIndices);
    return [...cellPatches, ...rangePatches, ...rowPatches, ...columnPatches];
}
export function materializeChangedCells(args, changedCellIndices) {
    return materializeChangedCellPatches(args, changedCellIndices);
}
//# sourceMappingURL=materialize-changed-cells.js.map