"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.serializeStepsToJson = serializeStepsToJson;
exports.applyStepEditsToSteps = applyStepEditsToSteps;
const mablscript_step_types_1 = require("../sharedUtils/mablscript-step-types");
const stepSerialization_1 = require("../sharedUtils/mablscript/stepSerialization");
const variableCanonical_1 = require("@mablhq/test-model/variableCanonical");
const editing_1 = require("@mablhq/test-model/editing");
const validator_1 = require("@mablhq/test-model/validator");
function serializeStepsToJson(steps, variables) {
    const aliasedVariables = (0, variableCanonical_1.aliasVariables)(variables);
    return steps.map((step) => (0, stepSerialization_1.parseStepToV2Format)(step, false, variables, aliasedVariables));
}
function applyStepEditsToSteps(input) {
    const { steps, variables, snippetsByInvariantId, capturedSelectors, editOps } = input;
    const currentJson = serializeStepsToJson(steps, variables);
    const { ops: safeOps, errors: findErrors } = (0, editing_1.enforceFindEditPolicy)(currentJson, editOps, {
        knownFindUuids: new Set(Object.keys(capturedSelectors)),
        resolveSelector: (uuid) => {
            const selector = capturedSelectors[uuid]?.holder?.selector;
            return selector
                ? { ...selector }
                : undefined;
        },
    });
    if (findErrors.length > 0) {
        return { outcome: { success: false, opErrors: findErrors } };
    }
    const { steps: rewritten, errors: opErrors } = (0, editing_1.applyEdits)(currentJson, safeOps);
    if (opErrors.length > 0) {
        return { outcome: { success: false, opErrors } };
    }
    const { steps: canonicalSteps, report } = (0, editing_1.canonicalizeStepsForSave)(rewritten);
    const flatLocatorErrors = (0, editing_1.rejectFlatLocator)(canonicalSteps);
    if (flatLocatorErrors.length > 0) {
        return { outcome: { success: false, flatLocatorErrors } };
    }
    const validation = (0, validator_1.validate)({ steps: canonicalSteps });
    if (!validation.valid) {
        return { outcome: { success: false, validationErrors: validation.errors } };
    }
    const currentById = new Map();
    const collectCurrent = (nodes) => {
        for (const step of nodes) {
            if (step.id) {
                currentById.set(step.id, step);
            }
            if ((0, mablscript_step_types_1.isStepGroupStep)(step) && step.stepsInGroup) {
                collectCurrent(step.stepsInGroup);
            }
        }
    };
    collectCurrent(steps);
    const bodyKey = (node) => {
        if (!(0, editing_1.getJsonStepGroupChildren)(node)) {
            return JSON.stringify(node);
        }
        const typeKey = Object.keys(node)[0];
        const body = node[typeKey];
        return JSON.stringify({ [typeKey]: { ...body, steps: undefined } });
    };
    const origBodyById = new Map();
    const collectBodies = (nodes) => {
        for (const node of nodes) {
            const id = (0, editing_1.getJsonStepId)(node);
            if (id) {
                origBodyById.set(id, bodyKey(node));
            }
            const kids = (0, editing_1.getJsonStepGroupChildren)(node);
            if (kids) {
                collectBodies(kids);
            }
        }
    };
    collectBodies(currentJson);
    const persistedVariables = {
        ...variables,
    };
    const reparse = (node) => (0, stepSerialization_1.parseFormattedStepToBaseStep)(node, variables, false, snippetsByInvariantId);
    const reconcile = (nodes) => nodes.map((node) => {
        const id = (0, editing_1.getJsonStepId)(node);
        const orig = id ? currentById.get(id) : undefined;
        const unchangedShell = orig !== undefined &&
            id !== undefined &&
            origBodyById.get(id) === bodyKey(node);
        if ((0, editing_1.getJsonStepGroupChildren)(node)) {
            const children = reconcile((0, editing_1.getJsonStepGroupChildren)(node));
            if (unchangedShell && (0, mablscript_step_types_1.isStepGroupStep)(orig)) {
                orig.stepsInGroup = children;
                return orig;
            }
            return reparse(node);
        }
        return unchangedShell ? orig : reparse(node);
    });
    let newSteps;
    try {
        newSteps = reconcile(canonicalSteps);
    }
    catch (error) {
        const deserializationError = {
            path: [],
            message: error instanceof Error
                ? `Failed to deserialize edited steps: ${error.message}`
                : 'Failed to deserialize edited steps',
            code: 'step_deserialization_failed',
        };
        return {
            outcome: { success: false, validationErrors: [deserializationError] },
        };
    }
    Object.assign(variables, persistedVariables);
    return { outcome: { success: true, canonicalization: report }, steps: newSteps };
}
