import { isDerivedVariable, isResolvedDerivedVariable, isResolvedSwitchVariable, } from '../../types';
import { isLoopVariable } from '../../types/utils';
function isPlaceholder(value) {
    return typeof value === 'object' && '__ref' in value;
}
/**
 * Serialize a nested key for identifier computation.
 * Handles both string keys and LoopVariable objects.
 */
function serializeNestedKey(key) {
    if (typeof key === 'string') {
        return key;
    }
    // It's a LoopVariable
    if (isLoopVariable(key)) {
        const loopNested = key.nested.join(',');
        return `LoopVar:${key.uid}:${loopNested}`;
    }
    return String(key);
}
export function getIdentifier(variable) {
    let id = `${variable.__typename}:${variable.uid}`;
    if ('nested' in variable && variable.nested.length > 0) {
        id += `:${variable.nested.map(serializeNestedKey).join(',')}`;
    }
    return id;
}
/**
 * Get an iterable of entries of array or object
 *
 * @param obj json-like array or object
 */
function getEntries(obj) {
    if (Array.isArray(obj)) {
        return obj.entries();
    }
    return Object.entries(obj);
}
/**
 * Denormalize data by replacing Placeholders with objects from lookup
 *
 * @param obj JSON-like structure containing placeholders
 * @param lookup map of identifier -> referrable
 */
export function denormalize(obj, lookup) {
    if (!obj) {
        return obj;
    }
    if (isPlaceholder(obj)) {
        const referrable = lookup[obj.__ref];
        return denormalize(referrable, lookup);
    }
    // Primitives (strings, numbers, booleans) should be returned as-is
    if (typeof obj !== 'object') {
        return obj;
    }
    const output = Array.isArray(obj) ? [] : {};
    for (const [key, val] of getEntries(obj)) {
        if (val !== null && typeof val === 'object') {
            output[key] = denormalize(val, lookup);
        }
        else {
            output[key] = val;
        }
    }
    return output;
}
/**
 * Normalize a ResolvedDerivedVariable object
 *
 * @param resolved resolved derived variable object
 * @param def variable definition
 */
function normalizeResolvedDerivedVariable(resolved, def) {
    // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
    const normalizedValues = [];
    let lookup = {};
    for (const [key, val] of resolved.values.entries()) {
        // Recursively call normalize on resolve derived variables
        if (isResolvedDerivedVariable(val)) {
            const { data: nestedNormalized, lookup: nestedLookup } = normalizeResolvedDerivedVariable(val, def.variables[key]);
            normalizedValues.push(nestedNormalized);
            lookup = Object.assign(Object.assign({}, lookup), nestedLookup);
        }
        else if (isResolvedSwitchVariable(val)) {
            // For switch variables, normalize the constituent parts
            const varDef = def.variables[key];
            const identifier = getIdentifier(varDef);
            lookup[identifier] = val;
            normalizedValues.push({ __ref: identifier });
        }
        else {
            // Put values into lookup
            const varDef = def.variables[key];
            const identifier = getIdentifier(varDef);
            lookup[identifier] = val === undefined ? null : val;
            normalizedValues.push({ __ref: identifier });
        }
    }
    return {
        data: Object.assign(Object.assign({}, resolved), { values: normalizedValues }),
        lookup,
    };
}
/**
 * Normalize py_component request data into a NormalizedPayload.
 * Puts all variable values into a lookup object to prevent data duplication.
 *
 * @param values resolved values
 * @param kwargsDefinition raw kwarg definition object
 */
export function normalizeRequest(values, kwargsDefinition) {
    const data = Array.isArray(values) ? [] : {};
    let lookup = {};
    // Iterate through both kwargs def and values
    // Where a plain Variable is found, put value to lookup
    for (const [key, val] of getEntries(values)) {
        const kwargDef = kwargsDefinition[key];
        // If no def is found, i.e. a static kwarg in PyComponent
        if (!kwargDef) {
            data[key] = val;
        }
        else if (isDerivedVariable(kwargDef)) {
            // Handle resolved derived variable
            const { data: nestedData, lookup: nestedLookup } = normalizeResolvedDerivedVariable(val, kwargDef);
            data[key] = nestedData;
            lookup = Object.assign(Object.assign({}, lookup), nestedLookup);
        }
        else if (kwargDef.constructor === Object) {
            const identifier = getIdentifier(kwargDef);
            lookup[identifier] = val === undefined ? null : val;
            data[key] = { __ref: identifier };
        }
        else {
            // If constructor is not an Object then it is the case of normalizing a static value
            data[key] = val;
        }
    }
    return {
        data,
        lookup,
    };
}
//# sourceMappingURL=normalization.js.map