var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
import {} from 'recoil';
import {} from '../../api';
import {} from '../../api/http';
import { isCondition, isDerivedVariable, isResolvedDerivedVariable, isResolvedSwitchVariable, isServerVariable, isStateVariable, isStreamVariable, isSwitchVariable, isVariable, } from '../../types';
// eslint-disable-next-line import/no-cycle
import { getOrRegisterDerivedVariable, getOrRegisterPlainVariable, resolvePlainVariableStatic } from './internal';
import { getOrRegisterServerVariable, resolveServerVariable, resolveServerVariableStatic } from './server-variable';
import { getOrRegisterStreamVariable } from './stream-variable';
/**
 * Error thrown when a variable cannot be resolved during preloading.
 * This signals to the preloader to skip this variable and resolve it at runtime.
 */
export class PreloadSkipError extends Error {
    constructor(message) {
        super(message);
        this.name = 'PreloadSkipError';
    }
}
export function resolveVariable(variable_1, client_1, taskContext_1, extras_1) {
    return __awaiter(this, arguments, void 0, function* (variable, client, taskContext, extras, resolver = (state) => Promise.resolve(state)) {
        if (isDerivedVariable(variable)) {
            getOrRegisterDerivedVariable(variable, client, taskContext, extras);
            // For derived variable, recursively resolve the dependencies
            const values = yield Promise.all(variable.variables.map((v) => resolveVariable(v, client, taskContext, extras, resolver)));
            // Store indexes of values which are in deps
            const deps = variable.deps.map((dep) => variable.variables.findIndex((v) => v.uid === dep.uid));
            return {
                deps,
                type: 'derived',
                uid: variable.uid,
                values,
                // After templating, nested should be all strings
                nested: variable.nested,
            };
        }
        if (isServerVariable(variable)) {
            getOrRegisterServerVariable(variable, extras);
            return resolveServerVariable(variable, extras, resolver);
        }
        if (isSwitchVariable(variable)) {
            // For switch variables, we need to resolve the constituent parts
            // and return a serialized representation similar to derived variables
            let resolvedValue = isVariable(variable.value) ?
                yield resolveVariable(variable.value, client, taskContext, extras, resolver)
                : variable.value;
            // value could be a condition object, resolve its variable
            if (isCondition(resolvedValue)) {
                resolvedValue = Object.assign(Object.assign({}, resolvedValue), { variable: (yield resolveVariable(resolvedValue.variable, client, taskContext, extras, resolver)) });
            }
            const resolvedValueMap = isVariable(variable.value_map) ?
                yield resolveVariable(variable.value_map, client, taskContext, extras, resolver)
                : variable.value_map;
            const resolvedDefault = isVariable(variable.default) ?
                yield resolveVariable(variable.default, client, taskContext, extras, resolver)
                : variable.default;
            return {
                type: 'switch',
                uid: variable.uid,
                value: resolvedValue,
                value_map: resolvedValueMap,
                default: resolvedDefault,
            };
        }
        if (isStateVariable(variable)) {
            // StateVariables should not be resolved as they are internal client-side variables
            // They should be handled by useVariable hook directly
            throw new Error('StateVariable should not be resolved - it should be handled by useVariable hook');
        }
        if (isStreamVariable(variable)) {
            // StreamVariable returns a Recoil selector that can be used as a dependency
            return resolver(getOrRegisterStreamVariable(variable, client, taskContext, extras));
        }
        return resolver(getOrRegisterPlainVariable(variable, client, taskContext, extras));
    });
}
/**
 * Static variant of variable resolution.
 * Notable does not register variables if they haven't yet been registered.
 * For plain variables, uses default values if not yet registered.
 */
export function resolveVariableStatic(variable, snapshot, params) {
    if (isDerivedVariable(variable)) {
        // For derived variable, recursively resolve the dependencies
        const values = variable.variables.map((v) => resolveVariableStatic(v, snapshot, params));
        // Store indexes of values which are in deps
        const deps = variable.deps.map((dep) => variable.variables.findIndex((v) => v.uid === dep.uid));
        return {
            deps,
            type: 'derived',
            uid: variable.uid,
            values,
            // After templating, nested should be all strings
            nested: variable.nested,
        };
    }
    if (isServerVariable(variable)) {
        return resolveServerVariableStatic(variable, snapshot);
    }
    if (isSwitchVariable(variable)) {
        // For switch variables, we need to resolve the constituent parts
        // and return a serialized representation similar to derived variables
        let resolvedValue = isVariable(variable.value) ? resolveVariableStatic(variable.value, snapshot, params) : variable.value;
        // value could be a condition object, resolve its variable
        if (isCondition(resolvedValue)) {
            resolvedValue = Object.assign(Object.assign({}, resolvedValue), { variable: resolveVariableStatic(resolvedValue.variable, snapshot, params) });
        }
        const resolvedValueMap = isVariable(variable.value_map) ?
            resolveVariableStatic(variable.value_map, snapshot, params)
            : variable.value_map;
        const resolvedDefault = isVariable(variable.default) ? resolveVariableStatic(variable.default, snapshot, params) : variable.default;
        return {
            type: 'switch',
            uid: variable.uid,
            value: resolvedValue,
            value_map: resolvedValueMap,
            default: resolvedDefault,
        };
    }
    if (isStateVariable(variable)) {
        // StateVariables cannot be preloaded - they track parent DerivedVariable state at runtime
        throw new PreloadSkipError('StateVariable cannot be used in on_load actions or preloaded. ' +
            'StateVariables track DerivedVariable loading/error state and are only available at runtime.');
    }
    if (isStreamVariable(variable)) {
        // StreamVariables cannot be preloaded - they receive data via SSE at runtime
        throw new PreloadSkipError('StreamVariable cannot be used in on_load actions or preloaded. ' +
            'StreamVariables receive data via server-sent events and are only available at runtime.');
    }
    // plain variable
    let result = resolvePlainVariableStatic(variable, snapshot, params);
    // unwrap if variable.default is a derived variable, i.e. used create_from_derived
    while (isDerivedVariable(result)) {
        result = resolveVariableStatic(result, snapshot, params);
    }
    return result;
}
/**
 * Clean value to a format understood by the backend.
 * Removes `deps`, preserves embedded `force_key` from resolved derived(data)variables.
 *
 * @param value a value to clean
 * @param forceKeyOverride optional force key to use instead of the one embedded in the resolved variable
 */
export function cleanValue(value, forceKeyOverride) {
    if (isResolvedDerivedVariable(value)) {
        const { deps } = value, rest = __rest(value, ["deps"]);
        const cleanedValues = value.values.map((v) => cleanValue(v));
        return Object.assign(Object.assign({}, rest), { 
            // Use override if provided, otherwise use the embedded force_key from the resolved variable
            force_key: forceKeyOverride !== null && forceKeyOverride !== void 0 ? forceKeyOverride : (value.force_key || null), values: cleanedValues });
    }
    if (isResolvedSwitchVariable(value)) {
        return Object.assign(Object.assign({}, value), { value: cleanValue(value.value, forceKeyOverride), value_map: cleanValue(value.value_map, forceKeyOverride), default: cleanValue(value.default, forceKeyOverride) });
    }
    return value;
}
/**
 * Clean kwargs to a format understood by the backend.
 * Removes `deps`, preserves embedded `force_key` from resolved derived(data)variables.
 *
 * @param kwargs kwargs to clean
 * @param forceKeyOverride optional force key to use instead of the one embedded in the resolved variable
 */
export function cleanKwargs(kwargs, forceKeyOverride) {
    return Object.keys(kwargs).reduce((acc, k) => {
        acc[k] = cleanValue(kwargs[k], forceKeyOverride);
        return acc;
    }, {});
}
/**
 * Format values into a shape expected by the backend.
 * Removes `deps`, preserves embedded `force_key` from resolved derived(data)variables.
 *
 * @param values list of values - plain values or ResolvedDerivedVariable constructs with plain values nested inside
 * @param forceKeyOverride optional force key to use instead of the one embedded in the resolved variable
 */
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
export function cleanArgs(values, forceKeyOverride) {
    return values.map((val) => cleanValue(val, forceKeyOverride));
}
//# sourceMappingURL=resolve-variable.js.map