import { formatAddress } from '@bilig/formula';
import { literalToValue, writeLiteralToCellStore } from '../../engine-value-utils.js';
import { exactLookupLiteralNumericValue, withOptionalLookupStringIds } from './direct-lookup-helpers.js';
import { directScalarLiteralNumericValue } from './direct-scalar-helpers.js';
import { applyOperationLookupNumericWriteTailPatches, planOperationLookupNumericWrites } from './operation-lookup-write-plans.js';
import { applyTableHeaderRenameForSetCellValue, isWorkbookTableHeaderCell } from './operation-table-header-rename.js';
export function applySetCellValueMutation(request) {
    const args = request.serviceArgs;
    const { existingIndex, isRestore, mutation, sheetId, sheetName } = request;
    const { hasAggregateDependents, hasExactLookupDependents, hasSortedLookupDependents } = request.dependencyFlags;
    let changedInputCount = request.changedInputCount;
    let formulaChangedCount = request.formulaChangedCount;
    let explicitChangedCount = request.explicitChangedCount;
    let topologyChanged = request.topologyChanged;
    let effectiveValue = mutation.value;
    const markReplicaVersion = () => {
        if (!isRestore && args.state.trackReplicaVersions) {
            request.setCellEntityVersion(sheetName, formatAddress(mutation.row, mutation.col), request.order);
        }
    };
    const rebindValueSensitiveDependents = (cellIndex) => {
        const rebound = request.rebindValueSensitiveFormulaDependents(cellIndex, {
            changedInputCount,
            formulaChangedCount,
            explicitChangedCount,
            topologyChanged,
        });
        changedInputCount = rebound.changedInputCount;
        formulaChangedCount = rebound.formulaChangedCount;
        explicitChangedCount = rebound.explicitChangedCount;
        topologyChanged = rebound.topologyChanged;
    };
    if (mutation.value === null &&
        !isRestore &&
        (existingIndex === undefined || request.isNullLiteralWriteNoOp(existingIndex)) &&
        !isWorkbookTableHeaderCell(args.state.workbook, sheetName, mutation.row, mutation.col)) {
        return { changedInputCount, formulaChangedCount, explicitChangedCount, topologyChanged };
    }
    if (!isRestore) {
        ;
        ({
            formulaChangedCount,
            topologyChanged,
            value: effectiveValue,
        } = applyTableHeaderRenameForSetCellValue({
            serviceArgs: args,
            sheetName,
            row: mutation.row,
            col: mutation.col,
            value: mutation.value,
            formulaChangedCount,
            topologyChanged,
        }));
    }
    const canFastOverwriteExisting = existingIndex !== undefined && request.canFastPathLiteralOverwrite(existingIndex);
    const needsDirectLookupNumericValue = canFastOverwriteExisting;
    const oldExactLookupNumber = canFastOverwriteExisting && hasExactLookupDependents ? request.readExactNumericValueForLookup(existingIndex) : undefined;
    const newExactLookupNumber = hasExactLookupDependents || needsDirectLookupNumericValue ? exactLookupLiteralNumericValue(effectiveValue) : undefined;
    const oldApproximateLookupNumber = canFastOverwriteExisting && hasSortedLookupDependents ? request.readApproximateNumericValueForLookup(existingIndex) : undefined;
    const newApproximateLookupNumber = hasSortedLookupDependents || needsDirectLookupNumericValue ? directScalarLiteralNumericValue(effectiveValue) : undefined;
    const lookupWritePlans = planOperationLookupNumericWrites({
        isRestore,
        hasAggregateDependents,
        hasExactLookupDependents,
        hasSortedLookupDependents,
        sheetId,
        sheetName,
        row: mutation.row,
        col: mutation.col,
        oldExactLookupNumber,
        newExactLookupNumber,
        oldApproximateLookupNumber,
        newApproximateLookupNumber,
        planner: {
            planExactLookupNumericColumnWrite: request.planExactLookupNumericColumnWrite,
            planApproximateLookupNumericColumnWrite: request.planApproximateLookupNumericColumnWrite,
        },
    });
    const exactLookupDependentsHandled = lookupWritePlans.exactHandled;
    const sortedLookupDependentsHandled = lookupWritePlans.sortedHandled;
    const needsLookupValueRead = lookupWritePlans.needsLookupValueRead;
    let directDependentsHandled = false;
    if (!isRestore && canFastOverwriteExisting) {
        const oldNumber = request.directScalarCellNumericValue(existingIndex);
        const newNumber = directScalarLiteralNumericValue(effectiveValue);
        if (oldNumber !== undefined && newNumber !== undefined) {
            directDependentsHandled = request.markPostRecalcDirectScalarNumericDependents(existingIndex, oldNumber, newNumber, request.postRecalcDirectFormulaIndices, newExactLookupNumber, newApproximateLookupNumber);
        }
    }
    const canUseDirectLookupCurrent = !isRestore &&
        canFastOverwriteExisting &&
        (newExactLookupNumber !== undefined || newApproximateLookupNumber !== undefined) &&
        !needsLookupValueRead &&
        !directDependentsHandled;
    if (canUseDirectLookupCurrent) {
        directDependentsHandled = request.markPostRecalcDirectLookupCurrentDependentsFromNumeric(existingIndex, newExactLookupNumber, newApproximateLookupNumber, request.postRecalcDirectFormulaIndices);
    }
    let prior = needsLookupValueRead || !directDependentsHandled ? request.readCellValueForLookup(existingIndex) : undefined;
    const markDirectFormulaDependents = (cellIndex, newValue) => {
        if (isRestore || directDependentsHandled || newValue === undefined) {
            return;
        }
        prior ??= request.readCellValueForLookup(existingIndex);
        const genericDirectDependentsHandled = request.markPostRecalcDirectFormulaDependents(cellIndex, request.postRecalcDirectFormulaIndices, prior.value, newValue);
        if (!genericDirectDependentsHandled) {
            request.markDirectScalarDeltaClosure(cellIndex, prior.value, newValue, request.postRecalcDirectFormulaIndices);
        }
    };
    const markLookupDependents = (cellIndex, newValue) => {
        if (!needsLookupValueRead) {
            return;
        }
        const newStringId = typeof effectiveValue === 'string' ? args.state.workbook.cellStore.stringIds[cellIndex] : undefined;
        const priorLookup = prior ?? request.readCellValueForLookup(existingIndex);
        const newLookupValue = newValue ?? literalToValue(effectiveValue, args.state.strings);
        if (hasExactLookupDependents || hasAggregateDependents) {
            const exactLookupRequest = withOptionalLookupStringIds({
                sheetName,
                row: mutation.row,
                col: mutation.col,
                oldValue: priorLookup.value,
                newValue: newLookupValue,
                oldStringId: priorLookup.stringId,
                newStringId,
                inputCellIndex: cellIndex,
            });
            if (hasExactLookupDependents && !exactLookupDependentsHandled) {
                formulaChangedCount = request.noteExactLookupLiteralWriteWhenDirty(exactLookupRequest, formulaChangedCount, request.exactLookupImpactCaches);
            }
            if (hasAggregateDependents) {
                args.noteAggregateLiteralWrite({
                    sheetName: exactLookupRequest.sheetName,
                    row: exactLookupRequest.row,
                    col: exactLookupRequest.col,
                    oldValue: exactLookupRequest.oldValue,
                    newValue: exactLookupRequest.newValue,
                });
                formulaChangedCount = request.markAffectedDirectRangeDependents(exactLookupRequest, formulaChangedCount, request.postRecalcDirectFormulaIndices);
            }
        }
        if (hasSortedLookupDependents) {
            const sortedLookupRequest = withOptionalLookupStringIds({
                sheetName,
                row: mutation.row,
                col: mutation.col,
                oldValue: priorLookup.value,
                newValue: newLookupValue,
                oldStringId: priorLookup.stringId,
                newStringId,
            });
            if (!sortedLookupDependentsHandled) {
                formulaChangedCount = request.noteSortedLookupLiteralWriteWhenDirty(sortedLookupRequest, formulaChangedCount);
            }
        }
    };
    if (canFastOverwriteExisting) {
        writeLiteralToCellStore(args.state.workbook.cellStore, existingIndex, effectiveValue, args.state.strings);
        args.state.workbook.notifyCellValueWritten(existingIndex);
        if (lookupWritePlans.handledLookupDependents) {
            const columnVersionAfterWrite = (args.state.workbook.getSheetById(sheetId)?.columnVersions[mutation.col] ?? 0) + 1;
            applyOperationLookupNumericWriteTailPatches(lookupWritePlans, {
                row: mutation.row,
                oldExactLookupNumber,
                newExactLookupNumber,
                oldApproximateLookupNumber,
                newApproximateLookupNumber,
                columnVersionAfterWrite,
            });
        }
        if (!isRestore) {
            rebindValueSensitiveDependents(existingIndex);
        }
        if (lookupWritePlans.handledLookupDependents) {
            request.queueHandledLookupInvalidation(sheetId, sheetName, mutation.col, hasExactLookupDependents && exactLookupDependentsHandled, hasSortedLookupDependents && sortedLookupDependentsHandled);
            if (!needsLookupValueRead) {
                request.noteHandledLookupInputCellIndex(existingIndex);
            }
        }
        const newValue = needsLookupValueRead || !directDependentsHandled ? literalToValue(effectiveValue, args.state.strings) : undefined;
        markDirectFormulaDependents(existingIndex, newValue);
        markLookupDependents(existingIndex, newValue);
        changedInputCount = args.markInputChanged(existingIndex, changedInputCount);
        if (request.trackExplicitChanges) {
            explicitChangedCount = args.markExplicitChanged(existingIndex, explicitChangedCount);
        }
        markReplicaVersion();
        return { changedInputCount, formulaChangedCount, explicitChangedCount, topologyChanged };
    }
    if (existingIndex !== undefined) {
        changedInputCount = args.markPivotRootsChanged(args.clearPivotForCell(existingIndex), changedInputCount);
    }
    const cellIndex = args.state.workbook.ensureCellAt(sheetId, mutation.row, mutation.col).cellIndex;
    if (!isRestore && existingIndex !== undefined) {
        const clearedSpill = args.clearSpillForCell(cellIndex);
        changedInputCount = args.markSpillRootsChanged(clearedSpill.changedCellIndices, changedInputCount);
        if (clearedSpill.ownerCellIndex !== undefined) {
            formulaChangedCount = args.markFormulaChanged(clearedSpill.ownerCellIndex, formulaChangedCount);
        }
        const removedFormula = args.removeFormula(cellIndex);
        topologyChanged = removedFormula || topologyChanged;
        if (removedFormula) {
            args.invalidateAggregateColumn({ sheetName, col: mutation.col });
            request.clearTrackedColumnDependencyFlagCache();
        }
    }
    writeLiteralToCellStore(args.state.workbook.cellStore, cellIndex, effectiveValue, args.state.strings);
    args.state.workbook.notifyCellValueWritten(cellIndex);
    if (!isRestore) {
        rebindValueSensitiveDependents(cellIndex);
    }
    const newValue = needsLookupValueRead || !directDependentsHandled ? literalToValue(effectiveValue, args.state.strings) : undefined;
    markDirectFormulaDependents(cellIndex, newValue);
    markLookupDependents(cellIndex, newValue);
    args.state.workbook.cellStore.flags[cellIndex] =
        (args.state.workbook.cellStore.flags[cellIndex] ?? 0) &
            ~(2 /* CellFlags.HasFormula */ | 4 /* CellFlags.JsOnly */ | 8 /* CellFlags.InCycle */ | 64 /* CellFlags.SpillChild */ | 128 /* CellFlags.PivotOutput */);
    if (!isRestore && effectiveValue === null) {
        request.pruneCellIfOrphaned(cellIndex);
    }
    changedInputCount = args.markInputChanged(cellIndex, changedInputCount);
    if (request.trackExplicitChanges) {
        explicitChangedCount = args.markExplicitChanged(cellIndex, explicitChangedCount);
    }
    markReplicaVersion();
    return { changedInputCount, formulaChangedCount, explicitChangedCount, topologyChanged };
}
//# sourceMappingURL=operation-set-cell-value-mutation.js.map