"use strict";
/**
 * Ported from https://github.com/optimizely/client-js/blob/7f84e6e779288d8870cac9e17309bb4a324ec5c2/src/core/lib/condition.js
 * Minor modifications to remove existing logs and avoid use of client-fn
 *
 * Test ['and', 'or', 'not'] conditions
 * @module
 */
/**
 * @enum {string}
 */
var Operator = {
    AND: 'and',
    OR: 'or',
    NOT: 'not',
};
/**
 * @typedef {(Operator|Condition)} ConditionOrOperator
 */
/**
 * @typedef {module:lib/condition~ConditionOrOperator[]} Conditions
 */
/**
 * All conditions in an "and" group must be satisfied.
 *
 * @private
 * @param {Condition[]} conditions
 * @param {evaluatorFn} evaluator Function called to evaluate condition objects
 * @returns {Boolean|undefined} True iff evaluator returns true for all conditions,
 * else false none are undefined, else undefined
 */
function and(conditions, evaluator) {
    var eachResult;
    var hasUndefined;
    // All conditions in an "and" group must be satisfied.
    for (var i = 0; i < conditions.length; i++) {
        eachResult = evaluate(conditions[i], evaluator);
        if (eachResult === false) {
            return false;
        }
        if (eachResult === undefined) {
            hasUndefined = true;
        }
    }
    // If we get to the end and nothing was false or undefined return true
    if (!hasUndefined) {
        return true;
    }
}
/**
 * Any condition in an "or" group may be satisfied.
 *
 * @private
 * @param {Condition[]} conditions
 * @param {evaluatorFn} evaluator Function called to evaluate condition objects
 * @return {Boolean|undefined} true if evaluator returns true for any condition, else false if all results are false,
 * else undefined if some results are undefined
 */
function or(conditions, evaluator) {
    var eachResult;
    var hasUndefined = false;
    for (var i = 0; i < conditions.length; i++) {
        eachResult = evaluate(conditions[i], evaluator);
        if (eachResult === true) {
            return true;
        }
        if (eachResult === undefined) {
            hasUndefined = true;
        }
    }
    // If we get to the end and nothing was true or undefined return false
    if (!hasUndefined) {
        return false;
    }
}
/**
 * No condition in a "not" group may be satisfied.
 *
 * @private
 * @param {Condition[]} singleCondition Condition(s) to evaluate (must have length 1).
 * @param {evaluatorFn} evaluator Function called to evaluate condition objects.
 * @return {Boolean|undefined} true if the condition evaluates false, false if the condition evaluates true, undefined
 * if the condition evaluates undefined
 */
function not(singleCondition, evaluator) {
    // No conditions in a "not" group may be satisfied.
    if (singleCondition.length !== 1) {
        return false;
    }
    var result = evaluate(singleCondition[0], evaluator);
    // If result is not undefined, return negated result
    if (result !== undefined) {
        return !result;
    }
}
var operators = {};
operators[Operator.AND] = and;
operators[Operator.OR] = or;
operators[Operator.NOT] = not;
/**
 * Determine if conditions are satisfied.
 *
 * Ported from condition.compoundTest in js/client/condition.js
 *
 * @param {module:lib/condition~Conditions} conditions Conditions to apply. Evaluates recursively.
 *        ```Grammar: conditions := [("and"|"or"),? (conditions)+] | ["not", conditions] | leaf
 *        leaf := POJO```
 * @param {evaluatorFn} evaluator
 * @return {Boolean|undefined} true/false if a result can be determined; undefined if no result can be determined
 * (because evaluator does not return a defined value for some required condition)
 * @throws {Error} Any error thrown by the evaluator at any point during the recursive evaluation.
 */
function evaluate(conditions, evaluator) {
    var result;
    if (Array.isArray(conditions)) {
        // This is a grouping of condition objects or condition arrays.
        var operator;
        var args;
        if (conditions[0] in operators) {
            // Operator to apply is explicit
            operator = conditions[0];
            args = conditions.slice(1);
        }
        else {
            // Operator to apply is not explicit - assume 'or'
            operator = Operator.OR;
            args = conditions;
        }
        result = operators[operator](args, evaluator);
        return result;
    }
    // apply the evaluator to the single condition
    result = evaluator(conditions);
    return result;
}
/**
 * @param {*} conditions
 *        ```Grammar: conditions := [("and"|"or"),? (conditions)+] | ["not", conditions] | leaf
 *        leaf := POJO```
 * @generator
 * @yields {Condition}
 *        The leaf values in the given conditions tree.
 */
function* getLeaves(conditions) {
    const isLeaf = !Array.isArray(conditions);
    if (isLeaf) {
        yield conditions;
        return;
    }
    // => `conditions` is a compound condition such as ['and', ...] or ['not', ...]
    let args;
    if (conditions[0] in operators) {
        // Operator to apply is explicit
        args = conditions.slice(1);
    }
    else {
        // Operator to apply is not explicit - assume 'or'
        args = conditions;
    }
    // `args` is a Conditions (an array of leaves or compound conditions)
    for (let arg of args) {
        yield* getLeaves(arg);
    }
}
const supportedConditionTypes = [
    'browser_version',
    'cookies',
    'device',
    'first_session',
    'ip',
    'query',
    'language',
    'location',
    'platform',
    'campaign',
    'referrer',
    'source_type',
    'time_and_day',
];
function allAudienceConditionsAreEdge(condition) {
    if ((condition || []).length === 0) {
        return true;
    }
    let unsupportedConditions = condition
        .flat(Infinity)
        .filter(cond => typeof cond === 'object')
        .filter(cond => !supportedConditionTypes.includes(cond.type.toLowerCase()));
    return unsupportedConditions.length === 0;
}
module.exports = {
    allAudienceConditionsAreEdge,
    evaluate,
    getLeaves,
};
