import { ErrorCode, ValueTag } from '@bilig/protocol';
import { cloneDefinedNameValue } from '../workbook-metadata-records.js';
import { definedNameKey } from '../workbook-metadata-types.js';
import { normalizeDefinedName } from '../workbook-store.js';
import { batchOpOrder, createBatch } from '../replica-state.js';
function namedNumberOperand(node, normalizedName, namedValue) {
    if (node.kind === 'NumberLiteral') {
        return node.value;
    }
    if (node.kind === 'NameRef' && normalizeDefinedName(node.name) === normalizedName) {
        return namedValue;
    }
    return undefined;
}
function evaluateNumericDefinedNameFormula(node, normalizedName, namedValue) {
    if (node.kind === 'NameRef' && normalizeDefinedName(node.name) === normalizedName) {
        return { tag: ValueTag.Number, value: namedValue };
    }
    if (node.kind !== 'BinaryExpr') {
        return undefined;
    }
    const left = namedNumberOperand(node.left, normalizedName, namedValue);
    const right = namedNumberOperand(node.right, normalizedName, namedValue);
    if (left === undefined || right === undefined) {
        return undefined;
    }
    if (node.operator === '+') {
        return { tag: ValueTag.Number, value: left + right };
    }
    if (node.operator === '-') {
        return { tag: ValueTag.Number, value: left - right };
    }
    if (node.operator === '*') {
        return { tag: ValueTag.Number, value: left * right };
    }
    if (node.operator === '/') {
        return right === 0 ? { tag: ValueTag.Error, code: ErrorCode.Div0 } : { tag: ValueTag.Number, value: left / right };
    }
    return undefined;
}
export function upsertNumericDefinedNameFast(args) {
    const trimmedName = args.name.trim();
    const normalizedName = normalizeDefinedName(trimmedName);
    const dependentCellIndices = args.collectDependentFormulaCells(normalizedName);
    const evaluated = [];
    for (let index = 0; index < dependentCellIndices.length; index += 1) {
        const cellIndex = dependentCellIndices[index];
        const formula = args.formulas.get(cellIndex);
        if (formula === undefined ||
            formula.compiled.symbolicNames.length !== 1 ||
            normalizeDefinedName(formula.compiled.symbolicNames[0]) !== normalizedName ||
            formula.compiled.symbolicRanges.length !== 0 ||
            formula.compiled.symbolicTables.length !== 0 ||
            formula.compiled.symbolicSpills.length !== 0) {
            return null;
        }
        const nextValue = evaluateNumericDefinedNameFormula(formula.compiled.ast, normalizedName, args.numericValue);
        if (nextValue === undefined) {
            return null;
        }
        evaluated.push({ cellIndex, value: nextValue });
    }
    const metadataKey = definedNameKey(trimmedName);
    const existing = args.workbook.metadata.definedNames.get(metadataKey);
    const op = { kind: 'upsertDefinedName', name: trimmedName, value: args.value };
    const batch = createBatch(args.replicaState, [op]);
    const order = batchOpOrder(batch, 0);
    args.workbook.metadata.definedNames.set(metadataKey, {
        name: trimmedName,
        value: cloneDefinedNameValue(args.value),
    });
    args.entityVersions.set(`defined-name:${normalizedName}`, order);
    const changedCellIndices = [];
    const cellStore = args.workbook.cellStore;
    for (let index = 0; index < evaluated.length; index += 1) {
        const { cellIndex, value: nextValue } = evaluated[index];
        const beforeTag = cellStore.tags[cellIndex] ?? ValueTag.Empty;
        const beforeNumber = cellStore.numbers[cellIndex] ?? 0;
        const beforeError = cellStore.errors[cellIndex] ?? ErrorCode.None;
        const changed = beforeTag !== nextValue.tag ||
            (nextValue.tag === ValueTag.Number && !Object.is(beforeNumber, nextValue.value)) ||
            (nextValue.tag === ValueTag.Error && beforeError !== nextValue.code);
        cellStore.flags[cellIndex] = (cellStore.flags[cellIndex] ?? 0) & ~(64 /* CellFlags.SpillChild */ | 128 /* CellFlags.PivotOutput */);
        cellStore.setValue(cellIndex, nextValue, 0);
        if (changed) {
            changedCellIndices.push(cellIndex);
        }
    }
    const inverseOp = existing === undefined
        ? { kind: 'deleteDefinedName', name: trimmedName }
        : { kind: 'upsertDefinedName', name: existing.name, value: cloneDefinedNameValue(existing.value) };
    args.undoStack.push({
        forward: { kind: 'single-op', op: { kind: 'upsertDefinedName', name: trimmedName, value: cloneDefinedNameValue(args.value) } },
        inverse: { kind: 'single-op', op: inverseOp },
    });
    args.redoStack.length = 0;
    return changedCellIndices;
}
//# sourceMappingURL=engine-numeric-defined-name-fast-path.js.map