"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setSnippetMetadata = exports.recalculateDefines = void 0;
const index_1 = require("./edge-decider/utils/index");
// TODO: Improve typing
function basicMerge(source, target) {
    for (const [key, val] of Object.entries(source)) {
        if (val !== null && typeof val === 'object') {
            target[key] ??= Object.create(Object.getPrototypeOf(val));
            basicMerge(val, target[key]);
        }
        else {
            target[key] = val;
        }
    }
}
const DEFINES_TO_CLEAN = [
    '__CHANGE__',
    '__TARGETING_CONDITIONS__',
    '__VIEW_FEATURES__',
    '__VIEW_TAG_FEATURES__',
    '__VIEW_TRIGGERS__',
    '__POLICY__',
];
function getFromMap(ids, entityList) {
    return entityList.filter(layer => ids.includes(layer.id));
}
// TODO: Create defines model
function recalculateDefines(baseDefines, config, queue, results, include_all = false) {
    let newDefines = {};
    const audiences = config.audiences;
    const layers = include_all
        ? config.layers
        : getFromMap(queue.undecidedLayerIds, config.layers);
    const views = include_all
        ? config.views
        : getFromMap(queue.unevaluatedViewsIds, config.views);
    // Clone defines
    basicMerge(baseDefines, newDefines);
    // Check if clean is needed
    DEFINES_TO_CLEAN.forEach(key => (newDefines[key] = {}));
    let newPolicies = new Set();
    let newChangeTypes = new Set();
    let newViewTriggers = new Set();
    let newViewTagLocator = new Set();
    let newViewFeature = new Set();
    let newTargetConditions = new Set();
    let tempAudienceIds = new Set();
    // Added change types for pending changes
    queue.unappliedChangesets
        .map(change => change.type?.toUpperCase())
        .forEach(type => newChangeTypes.add(type));
    layers.forEach(layer => {
        newPolicies.add(layer.policy.toUpperCase());
        layer.experiments.forEach(experiment => {
            (experiment.audienceIds || [])
                .slice(1)
                .forEach(audienceId => tempAudienceIds.add(audienceId));
            experiment.variations?.forEach(variation => {
                variation.actions?.forEach(action => {
                    action.changes?.forEach(change => newChangeTypes.add(change.type?.toUpperCase()));
                });
            });
            experiment.changes?.forEach(change => newChangeTypes.add(change.type?.toUpperCase()));
        });
    });
    views.forEach(view => {
        newViewTriggers.add((view.activationType || 'immediate').toUpperCase());
        view.tags?.forEach(tag => newViewTagLocator.add(tag.locatorType.toUpperCase()));
        (view.staticConditions || [])
            .flat(Infinity)
            .filter(cond => typeof cond === 'object')
            .forEach(cond => newViewFeature.add(cond.type.toUpperCase()));
    });
    audiences
        .filter(audience => tempAudienceIds.has(audience.id))
        .forEach(audience => {
        (audience.conditions || [])
            .flat(Infinity)
            .filter(cond => typeof cond === 'object')
            .forEach(cond => {
            let condType = cond.type;
            if (condType.startsWith('vendor.')) {
                condType = `third_party_${condType.substring(7)}`;
            }
            newTargetConditions.add(condType.toUpperCase());
        });
    });
    newDefines['__CHANGE__'] = Object.fromEntries(Array.from(newChangeTypes.values()).map(elem => [elem, true]));
    newDefines['__TARGETING_CONDITIONS__'] = Object.fromEntries(Array.from(newTargetConditions.values()).map(elem => [elem, true]));
    newDefines['__VIEW_FEATURES__'] = Object.fromEntries(Array.from(newViewFeature.values()).map(elem => [elem, true]));
    newDefines['__VIEW_TAG_FEATURES__'] = Object.fromEntries(Array.from(newViewTagLocator.values()).map(elem => [elem, true]));
    newDefines['__VIEW_TRIGGERS__'] = Object.fromEntries(Array.from(newViewTriggers.values()).map(elem => [elem, true]));
    newDefines['__POLICY__'] = Object.fromEntries(Array.from(newPolicies.values()).map(elem => [elem, true]));
    // Enabled snippet for web sdk mode
    newDefines['__EXPERIMENTAL_WEB_SDK__'] = true;
    //  Including all the config, maybe remove unused data in the future
    newDefines['__DATA__'] = (0, index_1.LiteralJSONStringify)(config);
    // New web_sdk_data format
    newDefines['__WEB_SDK_DATA__'] = (0, index_1.LiteralJSONStringify)({
        result: { ...cleanResults(results) },
        queue: { ...cleanQueue(queue) },
        metadata: (0, index_1.LiteralObject)('__EDGE_DECIDER_METADATA__'),
    });
    return newDefines;
}
exports.recalculateDefines = recalculateDefines;
/**
 * Inject logs and visitor data on the snippet string.
 * @param snippetStr
 * @param obj
 * @returns
 */
function setSnippetMetadata(snippetStr, obj) {
    const snippetMetadataStr = (0, index_1.LiteralJSONStringify)(obj || {});
    return snippetStr.replace('__EDGE_DECIDER_METADATA__', snippetMetadataStr);
}
exports.setSnippetMetadata = setSnippetMetadata;
function cleanResults(results) {
    let resultsCopy = structuredClone(results);
    resultsCopy.appliedChangesetsIds = resultsCopy.appliedChangesets.map(changeset => String(changeset.id));
    resultsCopy.appliedChangesets = [];
    return resultsCopy;
}
function cleanQueue(queue) {
    let queueCopy = structuredClone(queue);
    queueCopy.unappliedChangesetsIds = queueCopy.unappliedChangesets.map(changeset => String(changeset.id));
    queueCopy.unappliedChangesets = [];
    return queueCopy;
}
