import { Effect } from 'effect';
import { makeExactLookupColumnEntity, makeSortedLookupColumnEntity, entityPayload, isExactLookupColumnEntity, isRangeEntity, isSortedLookupColumnEntity, } from '../../entity-ids.js';
import { growUint32 } from '../../engine-buffer-utils.js';
import { EngineTraversalError } from '../errors.js';
import { ErrorCode, ValueTag } from '@bilig/protocol';
import { aggregateColumnDependencyKey, collectIndexedDirectAggregateColumnDependentsForRow } from './formula-binding-dependency-helpers.js';
const INITIAL_TRAVERSAL_SCRATCH_CAPACITY = 16;
const DIRECT_CRITERIA_AGGREGATE_FORMULA_DEPENDENCY_CACHE_LIMIT = 16_384;
function traversalErrorMessage(message, cause) {
    return cause instanceof Error && cause.message.length > 0 ? cause.message : message;
}
export function createEngineTraversalService(args) {
    const NO_ENTITY_DEPENDENT = -1;
    const MULTIPLE_ENTITY_DEPENDENTS = -2;
    let topoFormulaBuffer = new Uint32Array(INITIAL_TRAVERSAL_SCRATCH_CAPACITY);
    let topoEntityQueue = new Uint32Array(INITIAL_TRAVERSAL_SCRATCH_CAPACITY);
    let topoFormulaSeenEpoch = 1;
    let topoRangeSeenEpoch = 1;
    let topoExactLookupSeenEpoch = 1;
    let topoSortedLookupSeenEpoch = 1;
    let topoFormulaSeen = new Uint32Array(INITIAL_TRAVERSAL_SCRATCH_CAPACITY);
    let topoRangeSeen = new Uint32Array(INITIAL_TRAVERSAL_SCRATCH_CAPACITY);
    const topoExactLookupSeen = new Map();
    const topoSortedLookupSeen = new Map();
    let directRegionFormulaCacheVersion = -1;
    const directRegionFormulaMaxCellCache = new Map();
    const directCriteriaAggregateFormulaDependencyCache = new Map();
    const ensureTraversalScratchCapacity = (cellSize, entitySize, rangeSize) => {
        if (cellSize > topoFormulaBuffer.length) {
            topoFormulaBuffer = growUint32(topoFormulaBuffer, cellSize);
        }
        if (cellSize > topoFormulaSeen.length) {
            topoFormulaSeen = growUint32(topoFormulaSeen, cellSize);
        }
        if (entitySize > topoEntityQueue.length) {
            topoEntityQueue = growUint32(topoEntityQueue, entitySize);
        }
        if (rangeSize > topoRangeSeen.length) {
            topoRangeSeen = growUint32(topoRangeSeen, rangeSize);
        }
    };
    const ensureEntityQueueCapacity = (size) => {
        if (size <= topoEntityQueue.length) {
            return;
        }
        let capacity = topoEntityQueue.length;
        while (capacity < size) {
            capacity *= 2;
        }
        topoEntityQueue = growUint32(topoEntityQueue, capacity);
    };
    const ensureFormulaBufferCapacity = (size) => {
        if (size <= topoFormulaBuffer.length) {
            return;
        }
        let capacity = topoFormulaBuffer.length;
        while (capacity < size) {
            capacity *= 2;
        }
        topoFormulaBuffer = growUint32(topoFormulaBuffer, capacity);
    };
    const getReverseEdgeSlice = (entityId) => {
        if (isRangeEntity(entityId)) {
            return args.reverseState.reverseRangeEdges[entityPayload(entityId)];
        }
        if (isExactLookupColumnEntity(entityId)) {
            return args.reverseState.reverseExactLookupColumnEdges.get(entityPayload(entityId));
        }
        if (isSortedLookupColumnEntity(entityId)) {
            return args.reverseState.reverseSortedLookupColumnEdges.get(entityPayload(entityId));
        }
        return args.reverseState.reverseCellEdges[entityPayload(entityId)];
    };
    const getEntityDependentsNow = (entityId) => args.edgeArena.readView(getReverseEdgeSlice(entityId) ?? args.edgeArena.empty());
    const copySmallEntityDependentsNow = (entityId, target) => args.edgeArena.copyTo(getReverseEdgeSlice(entityId) ?? args.edgeArena.empty(), target);
    const getCellDependentsNow = (cellIndex) => args.edgeArena.readView(args.reverseState.reverseCellEdges[cellIndex] ?? args.edgeArena.empty());
    const getSingleCellDependentNow = (cellIndex) => {
        const slice = args.reverseState.reverseCellEdges[cellIndex];
        if (!slice || slice.len === 0 || slice.ptr < 0) {
            return NO_ENTITY_DEPENDENT;
        }
        if (slice.len !== 1) {
            return MULTIPLE_ENTITY_DEPENDENTS;
        }
        return args.edgeArena.singletonValue(slice);
    };
    const getSingleEntityDependentNow = (entityId) => {
        const slice = getReverseEdgeSlice(entityId);
        if (!slice || slice.len === 0 || slice.ptr < 0) {
            return NO_ENTITY_DEPENDENT;
        }
        if (slice.len !== 1) {
            return MULTIPLE_ENTITY_DEPENDENTS;
        }
        return args.edgeArena.singletonValue(slice);
    };
    const forEachFormulaDependencyCellNow = (cellIndex, fn) => {
        const formula = args.state.formulas.get(cellIndex);
        if (!formula) {
            return;
        }
        const seen = new Set();
        const push = (dependencyCellIndex) => {
            if (seen.has(dependencyCellIndex)) {
                return;
            }
            seen.add(dependencyCellIndex);
            fn(dependencyCellIndex);
        };
        const cellMatchesLiteral = (candidateCellIndex, expected) => {
            const store = args.state.workbook.cellStore;
            const tag = (candidateCellIndex === -1 ? ValueTag.Empty : (store.tags[candidateCellIndex] ?? ValueTag.Empty));
            if (tag !== expected.tag) {
                return false;
            }
            switch (expected.tag) {
                case ValueTag.Empty:
                    return true;
                case ValueTag.Number:
                    return Object.is(candidateCellIndex === -1 ? 0 : (store.numbers[candidateCellIndex] ?? 0), expected.value);
                case ValueTag.Boolean:
                    return (candidateCellIndex !== -1 && (store.numbers[candidateCellIndex] ?? 0) !== 0) === expected.value;
                case ValueTag.Error:
                    return ((candidateCellIndex === -1 ? ErrorCode.None : (store.errors[candidateCellIndex] ?? ErrorCode.None)) ===
                        expected.code);
                case ValueTag.String: {
                    const expectedStringId = expected.stringId ?? 0;
                    return expectedStringId !== 0
                        ? candidateCellIndex !== -1 && (store.stringIds[candidateCellIndex] ?? 0) === expectedStringId
                        : undefined;
                }
            }
        };
        const cellMatchesCriterion = (candidateCellIndex, criterion) => {
            if (criterion.kind === 'literal') {
                return cellMatchesLiteral(candidateCellIndex, criterion.value);
            }
            if (criterion.kind !== 'cell') {
                return undefined;
            }
            const store = args.state.workbook.cellStore;
            if (((store.flags[criterion.cellIndex] ?? 0) & 2 /* CellFlags.HasFormula */) !== 0) {
                return undefined;
            }
            const expected = store.getValue(criterion.cellIndex, () => '');
            return cellMatchesLiteral(candidateCellIndex, expected);
        };
        const directCriteriaRangeVersionKey = (range) => {
            const sheet = args.state.workbook.getSheet(range.sheetName);
            return `${range.sheetName}:${range.rowStart}:${range.rowEnd}:${range.col}:${sheet?.columnVersions[range.col] ?? 0}:${sheet?.structureVersion ?? 0}`;
        };
        const cellValueCacheKey = (sourceCellIndex) => {
            const store = args.state.workbook.cellStore;
            const tag = (sourceCellIndex === -1 ? ValueTag.Empty : (store.tags[sourceCellIndex] ?? ValueTag.Empty));
            switch (tag) {
                case ValueTag.Empty:
                    return 'e:';
                case ValueTag.Number:
                    return `n:${Object.is(sourceCellIndex === -1 ? 0 : (store.numbers[sourceCellIndex] ?? 0), -0) ? 0 : (store.numbers[sourceCellIndex] ?? 0)}`;
                case ValueTag.Boolean:
                    return sourceCellIndex !== -1 && (store.numbers[sourceCellIndex] ?? 0) !== 0 ? 'b:1' : 'b:0';
                case ValueTag.Error:
                    return `r:${(sourceCellIndex === -1 ? ErrorCode.None : (store.errors[sourceCellIndex] ?? ErrorCode.None))}`;
                case ValueTag.String: {
                    const stringId = sourceCellIndex === -1 ? 0 : (store.stringIds[sourceCellIndex] ?? 0);
                    return stringId === 0 ? undefined : `s:${stringId}`;
                }
            }
        };
        const directCriteriaOperandCacheKey = (criterion) => {
            if (criterion.kind === 'literal') {
                if (criterion.value.tag === ValueTag.String && (criterion.value.stringId ?? 0) === 0) {
                    return undefined;
                }
                switch (criterion.value.tag) {
                    case ValueTag.Empty:
                        return 'l:e:';
                    case ValueTag.Number:
                        return `l:n:${Object.is(criterion.value.value, -0) ? 0 : criterion.value.value}`;
                    case ValueTag.Boolean:
                        return criterion.value.value ? 'l:b:1' : 'l:b:0';
                    case ValueTag.Error:
                        return `l:r:${criterion.value.code}`;
                    case ValueTag.String:
                        return `l:s:${criterion.value.stringId}`;
                }
            }
            if (criterion.kind !== 'cell') {
                return undefined;
            }
            const store = args.state.workbook.cellStore;
            if (((store.flags[criterion.cellIndex] ?? 0) & 2 /* CellFlags.HasFormula */) !== 0) {
                return undefined;
            }
            const valueKey = cellValueCacheKey(criterion.cellIndex);
            return valueKey === undefined ? undefined : `c:${criterion.cellIndex}:${valueKey}`;
        };
        const directCriteriaAggregateFormulaDependencyCacheKey = (directCriteria) => {
            const aggregateRange = directCriteria.aggregateRange;
            if (!aggregateRange) {
                return undefined;
            }
            const pairKeys = [];
            for (let index = 0; index < directCriteria.criteriaPairs.length; index += 1) {
                const pair = directCriteria.criteriaPairs[index];
                const criterionKey = directCriteriaOperandCacheKey(pair.criterion);
                if (criterionKey === undefined) {
                    return undefined;
                }
                pairKeys.push(`${directCriteriaRangeVersionKey(pair.range)}:${criterionKey}`);
            }
            return `${args.state.formulas.version}\u0000${directCriteria.aggregateKind}\u0000${directCriteriaRangeVersionKey(aggregateRange)}\u0000${pairKeys.join('|')}`;
        };
        const rememberDirectCriteriaAggregateFormulaDependencyEntry = (key, entry) => {
            if (directCriteriaAggregateFormulaDependencyCache.size >= DIRECT_CRITERIA_AGGREGATE_FORMULA_DEPENDENCY_CACHE_LIMIT) {
                const firstKey = directCriteriaAggregateFormulaDependencyCache.keys().next().value;
                if (firstKey !== undefined) {
                    directCriteriaAggregateFormulaDependencyCache.delete(firstKey);
                }
            }
            directCriteriaAggregateFormulaDependencyCache.set(key, entry);
            return entry;
        };
        const pushMatchedDirectCriteriaAggregateFormulas = (directCriteria) => {
            const aggregateRange = directCriteria.aggregateRange;
            if (!aggregateRange) {
                return true;
            }
            const cacheKey = directCriteriaAggregateFormulaDependencyCacheKey(directCriteria);
            const cached = cacheKey === undefined ? undefined : directCriteriaAggregateFormulaDependencyCache.get(cacheKey);
            if (cached) {
                for (let index = 0; index < cached.dependencies.length; index += 1) {
                    push(cached.dependencies[index]);
                }
                return cached.canFilter;
            }
            const limitingPair = directCriteria.criteriaPairs.find((pair) => cellMatchesCriterion(-1, pair.criterion) === false);
            if (!limitingPair) {
                return false;
            }
            const aggregateSheet = args.state.workbook.getSheet(aggregateRange.sheetName);
            const limitingSheet = args.state.workbook.getSheet(limitingPair.range.sheetName);
            if (!aggregateSheet || !limitingSheet) {
                return false;
            }
            let canFilter = true;
            const matchedDependencies = [];
            const visitRowOffset = (rowOffset) => {
                if (!canFilter || rowOffset < 0 || rowOffset >= aggregateRange.length) {
                    return;
                }
                for (let pairIndex = 0; pairIndex < directCriteria.criteriaPairs.length; pairIndex += 1) {
                    const pair = directCriteria.criteriaPairs[pairIndex];
                    if (rowOffset >= pair.range.length) {
                        canFilter = false;
                        return;
                    }
                    const pairSheet = args.state.workbook.getSheet(pair.range.sheetName);
                    if (!pairSheet) {
                        canFilter = false;
                        return;
                    }
                    const criteriaCellIndex = pairSheet.grid.get(pair.range.rowStart + rowOffset, pair.range.col);
                    const matches = cellMatchesCriterion(criteriaCellIndex, pair.criterion);
                    if (matches === undefined) {
                        canFilter = false;
                        return;
                    }
                    if (!matches) {
                        return;
                    }
                }
                const aggregateCellIndex = aggregateSheet.grid.get(aggregateRange.rowStart + rowOffset, aggregateRange.col);
                if (aggregateCellIndex !== -1 && ((args.state.workbook.cellStore.flags[aggregateCellIndex] ?? 0) & 2 /* CellFlags.HasFormula */) !== 0) {
                    matchedDependencies.push(aggregateCellIndex);
                }
            };
            limitingSheet.grid.forEachInRange(limitingPair.range.rowStart, limitingPair.range.col, limitingPair.range.rowEnd, limitingPair.range.col, (criteriaCellIndex) => {
                visitRowOffset((args.state.workbook.cellStore.rows[criteriaCellIndex] ?? 0) - limitingPair.range.rowStart);
            });
            for (let index = 0; index < matchedDependencies.length; index += 1) {
                push(matchedDependencies[index]);
            }
            if (cacheKey !== undefined) {
                rememberDirectCriteriaAggregateFormulaDependencyEntry(cacheKey, { canFilter, dependencies: matchedDependencies });
            }
            return canFilter;
        };
        for (let index = 0; index < formula.dependencyIndices.length; index += 1) {
            push(formula.dependencyIndices[index]);
        }
        if (formula.directCriteria === undefined) {
            for (let index = 0; index < formula.graphRangeDependencies.length; index += 1) {
                const members = args.state.ranges.expandToCells(formula.graphRangeDependencies[index]);
                for (let memberIndex = 0; memberIndex < members.length; memberIndex += 1) {
                    push(members[memberIndex]);
                }
            }
        }
        const maxFormulaCellInDirectRegion = (regionId) => {
            const formulaVersion = args.state.formulas.version;
            if (directRegionFormulaCacheVersion !== formulaVersion) {
                directRegionFormulaCacheVersion = formulaVersion;
                directRegionFormulaMaxCellCache.clear();
            }
            if (directRegionFormulaMaxCellCache.has(regionId)) {
                return directRegionFormulaMaxCellCache.get(regionId);
            }
            const region = args.regionGraph.getRegion(regionId);
            const sheet = region ? args.state.workbook.getSheet(region.sheetName) : undefined;
            if (!region || !sheet) {
                directRegionFormulaMaxCellCache.set(regionId, undefined);
                return undefined;
            }
            let bestCellIndex;
            let bestRank = -1;
            for (let row = region.rowStart; row <= region.rowEnd; row += 1) {
                const dependencyCellIndex = sheet.grid.get(row, region.col);
                if (dependencyCellIndex === -1 || ((args.state.workbook.cellStore.flags[dependencyCellIndex] ?? 0) & 2 /* CellFlags.HasFormula */) === 0) {
                    continue;
                }
                const rank = args.state.workbook.cellStore.topoRanks[dependencyCellIndex] ?? 0;
                if (bestCellIndex === undefined || rank > bestRank || (rank === bestRank && dependencyCellIndex > bestCellIndex)) {
                    bestCellIndex = dependencyCellIndex;
                    bestRank = rank;
                }
            }
            directRegionFormulaMaxCellCache.set(regionId, bestCellIndex);
            return bestCellIndex;
        };
        const pushDirectRegion = (regionId) => {
            if (regionId === undefined) {
                return;
            }
            if (formula.directCriteria !== undefined) {
                const dependencyCellIndex = maxFormulaCellInDirectRegion(regionId);
                if (dependencyCellIndex !== undefined) {
                    push(dependencyCellIndex);
                }
                return;
            }
            const region = args.regionGraph.getRegion(regionId);
            if (!region) {
                return;
            }
            const sheet = args.state.workbook.getSheet(region.sheetName);
            if (!sheet) {
                return;
            }
            for (let row = region.rowStart; row <= region.rowEnd; row += 1) {
                const dependencyCellIndex = sheet.grid.get(row, region.col);
                if (dependencyCellIndex !== -1) {
                    push(dependencyCellIndex);
                }
            }
        };
        const pushDirectLookupRange = (range) => {
            if (!range) {
                return;
            }
            const sheet = args.state.workbook.getSheet(range.sheetName);
            if (!sheet) {
                return;
            }
            for (let row = range.rowStart; row <= range.rowEnd; row += 1) {
                const dependencyCellIndex = sheet.grid.get(row, range.col);
                if (dependencyCellIndex !== -1) {
                    push(dependencyCellIndex);
                }
            }
        };
        pushDirectRegion(formula.directAggregate?.regionId);
        if (formula.directCriteria) {
            if (!pushMatchedDirectCriteriaAggregateFormulas(formula.directCriteria)) {
                pushDirectRegion(formula.directCriteria.aggregateRange?.regionId);
            }
            for (let index = 0; index < formula.directCriteria.criteriaPairs.length; index += 1) {
                const pair = formula.directCriteria.criteriaPairs[index];
                pushDirectRegion(pair.range.regionId);
                if (pair.criterion.kind !== 'literal') {
                    push(pair.criterion.cellIndex);
                }
            }
        }
        const directLookup = formula.directLookup;
        if (directLookup) {
            if (directLookup.kind === 'exact' || directLookup.kind === 'approximate') {
                pushDirectLookupRange({
                    sheetName: directLookup.prepared.sheetName,
                    rowStart: directLookup.prepared.rowStart,
                    rowEnd: directLookup.prepared.rowEnd,
                    col: directLookup.prepared.col,
                });
                push(directLookup.operandCellIndex);
            }
            else {
                pushDirectLookupRange({
                    sheetName: directLookup.sheetName,
                    rowStart: directLookup.rowStart,
                    rowEnd: directLookup.rowEnd,
                    col: directLookup.col,
                });
                push(directLookup.operandCellIndex);
            }
        }
    };
    const forEachSheetCellNow = (sheetId, fn) => {
        const sheet = args.state.workbook.getSheetById(sheetId);
        if (!sheet) {
            return;
        }
        sheet.grid.forEachCellEntry((cellIndex, row, col) => {
            fn(cellIndex, row, col);
        });
    };
    const collectFormulaDependentsNow = (entityId) => {
        ensureTraversalScratchCapacity(Math.max(args.state.workbook.cellStore.size + 1, 1), Math.max(args.state.workbook.cellStore.size + args.state.ranges.size + 1, 1), Math.max(args.state.ranges.size + 1, 1));
        topoFormulaSeenEpoch += 1;
        if (topoFormulaSeenEpoch === 0xffff_ffff) {
            topoFormulaSeenEpoch = 1;
            topoFormulaSeen.fill(0);
        }
        topoRangeSeenEpoch += 1;
        if (topoRangeSeenEpoch === 0xffff_ffff) {
            topoRangeSeenEpoch = 1;
            topoRangeSeen.fill(0);
        }
        topoExactLookupSeenEpoch += 1;
        if (topoExactLookupSeenEpoch === 0xffff_ffff) {
            topoExactLookupSeenEpoch = 1;
            topoExactLookupSeen.clear();
        }
        topoSortedLookupSeenEpoch += 1;
        if (topoSortedLookupSeenEpoch === 0xffff_ffff) {
            topoSortedLookupSeenEpoch = 1;
            topoSortedLookupSeen.clear();
        }
        let entityQueueLength = 1;
        let formulaCount = 0;
        topoEntityQueue[0] = entityId;
        const pushFormulaDependent = (formulaCellIndex) => {
            if (topoFormulaSeen[formulaCellIndex] === topoFormulaSeenEpoch) {
                return;
            }
            topoFormulaSeen[formulaCellIndex] = topoFormulaSeenEpoch;
            ensureFormulaBufferCapacity(formulaCount + 1);
            topoFormulaBuffer[formulaCount] = formulaCellIndex;
            formulaCount += 1;
        };
        for (let queueIndex = 0; queueIndex < entityQueueLength; queueIndex += 1) {
            const currentEntity = topoEntityQueue[queueIndex];
            if (!isRangeEntity(currentEntity) && !isExactLookupColumnEntity(currentEntity) && !isSortedLookupColumnEntity(currentEntity)) {
                const cellIndex = entityPayload(currentEntity);
                const sheetId = args.state.workbook.cellStore.sheetIds[cellIndex];
                const position = args.state.workbook.getCellPosition(cellIndex);
                if (sheetId !== undefined && position) {
                    const regionDependents = args.regionGraph.collectFormulaDependentsForCell(sheetId, position.row, position.col);
                    for (let index = 0; index < regionDependents.length; index += 1) {
                        pushFormulaDependent(regionDependents[index]);
                    }
                }
                if (sheetId !== undefined && position) {
                    const aggregateDependents = args.reverseState.reverseAggregateColumnEdges.get(aggregateColumnDependencyKey(sheetId, position.col));
                    if (aggregateDependents) {
                        const indexedAggregateDependents = collectIndexedDirectAggregateColumnDependentsForRow(aggregateDependents, position.row);
                        if (indexedAggregateDependents) {
                            for (let index = 0; index < indexedAggregateDependents.length; index += 1) {
                                pushFormulaDependent(indexedAggregateDependents[index]);
                            }
                        }
                        else {
                            aggregateDependents.forEach((formulaCellIndex) => pushFormulaDependent(formulaCellIndex));
                        }
                    }
                    const exactLookupEntity = makeExactLookupColumnEntity(sheetId, position.col);
                    const sortedLookupEntity = makeSortedLookupColumnEntity(sheetId, position.col);
                    ensureEntityQueueCapacity(entityQueueLength + 2);
                    topoEntityQueue[entityQueueLength] = exactLookupEntity;
                    entityQueueLength += 1;
                    topoEntityQueue[entityQueueLength] = sortedLookupEntity;
                    entityQueueLength += 1;
                }
            }
            const dependents = getEntityDependentsNow(currentEntity);
            for (let index = 0; index < dependents.length; index += 1) {
                const dependent = dependents[index];
                if (!(isRangeEntity(dependent) || isExactLookupColumnEntity(dependent) || isSortedLookupColumnEntity(dependent))) {
                    pushFormulaDependent(entityPayload(dependent));
                    continue;
                }
                if (isRangeEntity(dependent)) {
                    const rangeIndex = entityPayload(dependent);
                    if (topoRangeSeen[rangeIndex] === topoRangeSeenEpoch) {
                        continue;
                    }
                    topoRangeSeen[rangeIndex] = topoRangeSeenEpoch;
                }
                else if (isExactLookupColumnEntity(dependent)) {
                    const lookupColumnPayload = entityPayload(dependent);
                    if (topoExactLookupSeen.get(lookupColumnPayload) === topoExactLookupSeenEpoch) {
                        continue;
                    }
                    topoExactLookupSeen.set(lookupColumnPayload, topoExactLookupSeenEpoch);
                }
                else {
                    const lookupColumnPayload = entityPayload(dependent);
                    if (topoSortedLookupSeen.get(lookupColumnPayload) === topoSortedLookupSeenEpoch) {
                        continue;
                    }
                    topoSortedLookupSeen.set(lookupColumnPayload, topoSortedLookupSeenEpoch);
                }
                ensureEntityQueueCapacity(entityQueueLength + 1);
                topoEntityQueue[entityQueueLength] = dependent;
                entityQueueLength += 1;
            }
        }
        return topoFormulaBuffer.subarray(0, formulaCount);
    };
    return {
        getEntityDependents(entityId) {
            return Effect.try({
                try: () => Uint32Array.from(getEntityDependentsNow(entityId)),
                catch: (cause) => new EngineTraversalError({
                    message: traversalErrorMessage('Failed to read entity dependents', cause),
                    cause,
                }),
            });
        },
        getSingleEntityDependent(entityId) {
            return Effect.try({
                try: () => getSingleEntityDependentNow(entityId),
                catch: (cause) => new EngineTraversalError({
                    message: traversalErrorMessage('Failed to read single entity dependent', cause),
                    cause,
                }),
            });
        },
        collectFormulaDependents(entityId) {
            return Effect.try({
                try: () => Uint32Array.from(collectFormulaDependentsNow(entityId)),
                catch: (cause) => new EngineTraversalError({
                    message: traversalErrorMessage('Failed to collect formula dependents', cause),
                    cause,
                }),
            });
        },
        forEachFormulaDependencyCell(cellIndex, fn) {
            return Effect.try({
                try: () => {
                    forEachFormulaDependencyCellNow(cellIndex, fn);
                },
                catch: (cause) => new EngineTraversalError({
                    message: traversalErrorMessage('Failed to iterate formula dependencies', cause),
                    cause,
                }),
            });
        },
        forEachSheetCell(sheetId, fn) {
            return Effect.try({
                try: () => {
                    forEachSheetCellNow(sheetId, fn);
                },
                catch: (cause) => new EngineTraversalError({
                    message: traversalErrorMessage('Failed to iterate sheet cells', cause),
                    cause,
                }),
            });
        },
        getEntityDependentsNow,
        copySmallEntityDependentsNow,
        getSingleEntityDependentNow,
        getCellDependentsNow,
        getSingleCellDependentNow,
        collectFormulaDependentsNow,
        forEachFormulaDependencyCellNow,
        forEachSheetCellNow,
    };
}
//# sourceMappingURL=traversal-service.js.map