"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertToDatafile = exports.coerceGroupToFSFormat = exports.coerceExperimentToFSFormat = void 0;
/**
 * TODO(CJS-2806): Think of a better naming convention for this file, like `webmanifestV0_to_datafileV4`
 * TODO(CJS-2811): Implement schema validation
 *
 * SUPERSET OF V4 FULL STACK DATAFILE SCHEMA
 * Module for converting Web Manifest into superset of
 * Full Stack datafile schema, based on V4:
 * https://github.com/optimizely/optimizely/blob/5855e5ae5e705702134fee55f59bb7498573df40/src/www/services/datafile_generator/schema.json#L530
 */
const constants_1 = require("../../constants");
const error_1 = require("../error");
/**
 * Coerce experiments into Full Stack datafile-like format that
 * Full Stack SDKs can understand.
 *
 * Based on V4 of the Full Stack datafile schema
 * https://github.com/optimizely/optimizely/blob/5855e5ae5e705702134fee55f59bb7498573df40/src/www/services/datafile_generator/schema.json#L530
 * @param {Object} experiment
 * @returns {Object} experiment in FS format
 * @throws {Error} Will throw an error if no variations on experiment
 */
const coerceExperimentToFSFormat = exp => {
    const expToReturn = { ...exp };
    expToReturn.key = String(expToReturn.id);
    expToReturn.status = 'Running';
    expToReturn.trafficAllocation = expToReturn.weightDistributions;
    expToReturn.forcedVariations = {};
    // What is a Weight Distribution?
    // From https://github.com/optimizely/optimizely/blob/f401cc32aee762d35373b02bd2d7226219c1e67f/src/www/models/weight_distributions.py:
    // Model defining weight distributions. This model is meant to be
    // used in repeated StructuredProperty. When repeated, the range_end
    // defines where the distribution ends for that specific id in terms of
    // basis points (0 to 10000).
    // The Full Stack datafile schema ditched "weightDistributions" in
    // favor of "trafficAllocation".
    delete expToReturn.weightDistributions;
    if (!expToReturn.variations || !expToReturn.variations.length) {
        let newError = new error_1.DatafileError(`No variations on experiment ${expToReturn.id}`);
        throw newError;
    }
    if (!expToReturn.trafficAllocation) {
        expToReturn.trafficAllocation = [
            {
                entityId: expToReturn.variations[0].id,
                endOfRange: 10000,
            },
        ];
    }
    expToReturn.variations.forEach(variation => {
        variation.key = String(variation.id);
    });
    return expToReturn;
};
exports.coerceExperimentToFSFormat = coerceExperimentToFSFormat;
/**
 * Coerce groups into Full Stack datafile-like format that
 * Full Stack SDKs can understand.
 *
 * Based on V4 of the Full Stack datafile schema
 * https://github.com/optimizely/optimizely/blob/5855e5ae5e705702134fee55f59bb7498573df40/src/www/services/datafile_generator/schema.json#L468
 * @param {Object} group
 * @returns {Object} group in FS format
 */
const coerceGroupToFSFormat = group => {
    const groupToReturn = { ...group };
    groupToReturn.policy = 'random';
    const experimentsByLayerId = groupToReturn.experiments.reduce((result, experiment) => {
        result[experiment.layerId] = experiment;
        return result;
    }, {});
    groupToReturn.trafficAllocation = groupToReturn.weightDistributions.reduce((acc, wdObj) => {
        if (wdObj.entityId === 'None')
            return acc;
        /*
         * This condition was added due to CJS-6098
         * when there is a paused experiment in the exclusion group, the web_manifest keeps the reference of the paused experiment in the WeightDistributions section
         * but, the paused experiment is not included in the data, so it triggers the error: "Cannot read properties of undefined (reading 'id')"
         */
        if (!experimentsByLayerId[wdObj.entityId])
            return acc;
        wdObj.entityId = experimentsByLayerId[wdObj.entityId].id;
        acc.push(wdObj);
        return acc;
    }, []);
    // The Full Stack datafile schema ditched "weightDistributions" in
    // favor of "trafficAllocation".
    delete groupToReturn.weightDistributions;
    return groupToReturn;
};
exports.coerceGroupToFSFormat = coerceGroupToFSFormat;
/**
 * Return a datafile in Full Stack format.
 *
 * @param {Object} data - Web Manifest data in V0 format
 * @returns {{accountId: (string|*), anonymizeIP: boolean, botFiltering: boolean, projectId: (number|string), revision: string, version: string, audiences: any[], experiments: Array, layers: *, projectJS: string}}
 */
const convertToDatafile = manifest => {
    // TODO(CJS-2807): add validation for input data from web_manifest and output datafile.
    const data = Object.assign({ groups: [] }, structuredClone(manifest));
    const experimentsByType = data.layers
        .filter(l => l.policy === constants_1.ExperimentType.SINGLE_EXPERIMENT ||
        l.policy === constants_1.ExperimentType.EQUAL_PRIORITY ||
        l.policy === constants_1.ExperimentType.MULTIVARIATE)
        .flatMap(l => {
        if (!l.experiments || !l.experiments.length) {
            let newError = new error_1.DatafileError(`No experiments on layer ${l.id}`);
            throw newError;
        }
        return l.experiments.map(exp => {
            // We'll add the layerId as a convenience, as later on we need to look up
            // the layer that corresponds with each experiment to get the changes.
            exp.layerId = l.id;
            // Preserve the layer groupId to separate them later
            if (l.groupId) {
                exp.groupId = l.groupId;
            }
            // Add percentageIncluded field
            exp.percentageIncluded = 10000 - l.holdback;
            return exp;
        });
    })
        .reduce((acc, exp) => {
        if (exp.groupId) {
            if (acc.experimentsByGroupId[exp.groupId]) {
                acc.experimentsByGroupId[exp.groupId].push(exp);
            }
            else {
                acc.experimentsByGroupId[exp.groupId] = [exp];
            }
        }
        else {
            acc.individualExperiments.push(exp);
        }
        return acc;
    }, {
        experimentsByGroupId: {},
        individualExperiments: [],
    });
    const experiments = experimentsByType.individualExperiments.map(exports.coerceExperimentToFSFormat);
    const groups = data.groups.map(group => {
        const groupExperiments = experimentsByType.experimentsByGroupId[group.id];
        if (groupExperiments) {
            group.experiments = groupExperiments.map(exports.coerceExperimentToFSFormat);
        }
        return (0, exports.coerceGroupToFSFormat)(group);
    });
    return {
        /* CORE CONFIG */
        accountId: data.accountId,
        anonymizeIP: false,
        botFiltering: false,
        projectId: data.projectId,
        revision: data.revision,
        version: constants_1.DatafileVersion.V4,
        /* DATA ENTITIES */
        typedAudiences: data.audiences,
        experiments,
        groups,
        events: [],
        attributes: [],
        audiences: [],
    };
};
exports.convertToDatafile = convertToDatafile;
exports.default = {
    coerceExperimentToFSFormat: exports.coerceExperimentToFSFormat,
    convertToDatafile: exports.convertToDatafile,
};
