import { getCellNumberFormatKind, } from '@bilig/protocol';
import { formatAddress, parseCellAddress } from '@bilig/formula';
import { cloneWorkbookRangeRecords, findWorkbookRangeRecord, overlayWorkbookRangeRecords, replaceWorkbookRangeRecords, } from './workbook-range-records.js';
import { cellNumberFormatIdForCode, cellStyleIdForKey, cellStyleKey, normalizeCellNumberFormatRecord, normalizeCellStyleRecord, } from './workbook-store-records.js';
export function upsertCellStyle(catalog, style, bumpStyleId) {
    const normalized = normalizeCellStyleRecord(style);
    const existing = catalog.cellStyles.get(normalized.id);
    if (existing) {
        catalog.styleKeys.delete(cellStyleKey(existing));
    }
    catalog.cellStyles.set(normalized.id, normalized);
    catalog.styleKeys.set(cellStyleKey(normalized), normalized.id);
    bumpStyleId(normalized.id);
    return normalized;
}
export function internCellStyle(catalog, style, defaultStyleId) {
    const normalized = normalizeCellStyleRecord({
        id: defaultStyleId,
        ...style,
    });
    const key = cellStyleKey(normalized);
    const existingId = catalog.styleKeys.get(key);
    if (existingId) {
        return catalog.cellStyles.get(existingId);
    }
    return { ...normalized, id: cellStyleIdForKey(key) };
}
export function getCellStyle(catalog, id, defaultStyleId) {
    if (!id) {
        return catalog.cellStyles.get(defaultStyleId);
    }
    return catalog.cellStyles.get(id) ?? catalog.cellStyles.get(defaultStyleId);
}
export function listCellStyles(catalog) {
    return [...catalog.cellStyles.values()].toSorted((left, right) => left.id.localeCompare(right.id));
}
export function upsertCellNumberFormat(catalog, format, bumpFormatId) {
    const normalized = normalizeCellNumberFormatRecord(format);
    const existing = catalog.cellNumberFormats.get(normalized.id);
    if (existing) {
        catalog.numberFormatKeys.delete(existing.code);
    }
    catalog.cellNumberFormats.set(normalized.id, normalized);
    catalog.numberFormatKeys.set(normalized.code, normalized.id);
    bumpFormatId(normalized.id);
    return normalized;
}
export function internCellNumberFormat(catalog, format, defaultFormatId) {
    const normalized = typeof format === 'string'
        ? normalizeCellNumberFormatRecord({
            id: defaultFormatId,
            code: format,
            kind: getCellNumberFormatKind(format),
        })
        : normalizeCellNumberFormatRecord(format);
    const existingId = catalog.numberFormatKeys.get(normalized.code);
    if (existingId) {
        return catalog.cellNumberFormats.get(existingId);
    }
    return { ...normalized, id: cellNumberFormatIdForCode(normalized.code) };
}
export function getCellNumberFormat(catalog, id, defaultFormatId) {
    if (!id) {
        return catalog.cellNumberFormats.get(defaultFormatId);
    }
    return catalog.cellNumberFormats.get(id) ?? catalog.cellNumberFormats.get(defaultFormatId);
}
export function listCellNumberFormats(catalog) {
    return [...catalog.cellNumberFormats.values()].toSorted((left, right) => left.id.localeCompare(right.id));
}
export function setStyleRange(catalog, sheet, range, styleId, defaultStyleId) {
    if (!catalog.cellStyles.has(styleId)) {
        throw new Error(`Unknown cell style: ${styleId}`);
    }
    const stored = {
        range: { ...range },
        styleId,
    };
    sheet.styleRanges = overlayWorkbookRangeRecords(sheet.styleRanges, stored, (nextRange, record) => ({
        range: nextRange,
        styleId: record.styleId,
    }), (record) => record.styleId === defaultStyleId);
    return stored;
}
export function listStyleRanges(sheet) {
    return cloneWorkbookRangeRecords(sheet?.styleRanges ?? [], (range, record) => ({
        range,
        styleId: record.styleId,
    }));
}
export function setStyleRanges(catalog, sheet, ranges) {
    const nextRanges = replaceWorkbookRangeRecords(ranges.map((entry) => ({
        range: { ...entry.range },
        styleId: entry.styleId,
    })), (range, record) => ({
        range,
        styleId: record.styleId,
    }), (entry) => {
        if (!catalog.cellStyles.has(entry.styleId)) {
            throw new Error(`Unknown cell style: ${entry.styleId}`);
        }
    });
    sheet.styleRanges = nextRanges;
    return listStyleRanges(sheet);
}
export function getStyleId(sheet, row, col, defaultStyleId) {
    if (!sheet) {
        return defaultStyleId;
    }
    return findWorkbookRangeRecord(sheet.styleRanges, row, col)?.styleId ?? defaultStyleId;
}
export function setFormatRange(catalog, sheet, range, formatId, defaultFormatId) {
    if (!catalog.cellNumberFormats.has(formatId)) {
        throw new Error(`Unknown cell number format: ${formatId}`);
    }
    const stored = {
        range: { ...range },
        formatId,
    };
    sheet.formatRanges = overlayWorkbookRangeRecords(sheet.formatRanges, stored, (nextRange, record) => ({
        range: nextRange,
        formatId: record.formatId,
    }), (record) => record.formatId === defaultFormatId);
    return stored;
}
export function listFormatRanges(sheet) {
    return cloneWorkbookRangeRecords(sheet?.formatRanges ?? [], (range, record) => ({
        range,
        formatId: record.formatId,
    }));
}
export function setFormatRanges(catalog, sheet, ranges) {
    const nextRanges = replaceWorkbookRangeRecords(ranges.map((entry) => ({
        range: { ...entry.range },
        formatId: entry.formatId,
    })), (range, record) => ({
        range,
        formatId: record.formatId,
    }), (entry) => {
        if (!catalog.cellNumberFormats.has(entry.formatId)) {
            throw new Error(`Unknown cell number format: ${entry.formatId}`);
        }
    });
    sheet.formatRanges = nextRanges;
    return listFormatRanges(sheet);
}
export function getRangeFormatId(sheet, row, col, defaultFormatId) {
    if (!sheet) {
        return defaultFormatId;
    }
    return findWorkbookRangeRecord(sheet.formatRanges, row, col)?.formatId ?? defaultFormatId;
}
export function coalesceStyleRanges(sheet) {
    sheet.styleRanges = coalesceWorkbookRangeRecords(sheet.styleRanges, (left, right) => left.styleId === right.styleId, (range, record) => ({
        range,
        styleId: record.styleId,
    }));
    return listStyleRanges(sheet);
}
function coalesceWorkbookRangeRecords(records, canMerge, cloneRecord) {
    const sorted = records
        .map((record) => ({
        record,
        start: parseCellAddress(record.range.startAddress, record.range.sheetName),
        end: parseCellAddress(record.range.endAddress, record.range.sheetName),
    }))
        .toSorted((left, right) => {
        const sheetCompare = left.record.range.sheetName.localeCompare(right.record.range.sheetName);
        if (sheetCompare !== 0) {
            return sheetCompare;
        }
        if (left.start.row !== right.start.row) {
            return left.start.row - right.start.row;
        }
        if (left.start.col !== right.start.col) {
            return left.start.col - right.start.col;
        }
        if (left.end.row !== right.end.row) {
            return left.end.row - right.end.row;
        }
        return left.end.col - right.end.col;
    });
    const entries = sorted.map(({ record, start, end }) => {
        const startRow = Math.min(start.row, end.row);
        const endRow = Math.max(start.row, end.row);
        const startCol = Math.min(start.col, end.col);
        const endCol = Math.max(start.col, end.col);
        return {
            record: cloneRecord({
                sheetName: record.range.sheetName,
                startAddress: formatAddress(startRow, startCol),
                endAddress: formatAddress(endRow, endCol),
            }, record),
            startRow,
            endRow,
            startCol,
            endCol,
        };
    });
    let merged = entries;
    let changed = true;
    while (changed) {
        changed = false;
        const next = [];
        for (const entry of merged) {
            const mergeIndex = next.findIndex((candidate) => canMergeRangeEntries(candidate, entry, canMerge));
            if (mergeIndex === -1) {
                next.push(entry);
                continue;
            }
            next[mergeIndex] = mergeRangeEntries(next[mergeIndex], entry, cloneRecord);
            changed = true;
        }
        merged = next.toSorted((left, right) => left.record.range.sheetName.localeCompare(right.record.range.sheetName) ||
            left.startRow - right.startRow ||
            left.startCol - right.startCol ||
            left.endRow - right.endRow ||
            left.endCol - right.endCol);
    }
    return merged.map(({ record }) => record);
}
function canMergeRangeEntries(left, right, canMerge) {
    return (canMerge(left.record, right.record) &&
        left.record.range.sheetName === right.record.range.sheetName &&
        ((left.startCol === right.startCol && left.endCol === right.endCol && right.startRow <= left.endRow + 1) ||
            (left.startRow === right.startRow && left.endRow === right.endRow && right.startCol <= left.endCol + 1)));
}
function mergeRangeEntries(left, right, cloneRecord) {
    const startRow = Math.min(left.startRow, right.startRow);
    const endRow = Math.max(left.endRow, right.endRow);
    const startCol = Math.min(left.startCol, right.startCol);
    const endCol = Math.max(left.endCol, right.endCol);
    return {
        record: cloneRecord({
            sheetName: left.record.range.sheetName,
            startAddress: formatAddress(startRow, startCol),
            endAddress: formatAddress(endRow, endCol),
        }, left.record),
        startRow,
        endRow,
        startCol,
        endCol,
    };
}
//# sourceMappingURL=workbook-style-format-store.js.map