"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Strategy = exports.Operator = void 0;
const semver_1 = require("semver");
const helpers_1 = require("../helpers");
const variant_1 = require("../variant");
var Operator;
(function (Operator) {
    Operator["IN"] = "IN";
    Operator["NOT_IN"] = "NOT_IN";
    Operator["STR_ENDS_WITH"] = "STR_ENDS_WITH";
    Operator["STR_STARTS_WITH"] = "STR_STARTS_WITH";
    Operator["STR_CONTAINS"] = "STR_CONTAINS";
    Operator["NUM_EQ"] = "NUM_EQ";
    Operator["NUM_GT"] = "NUM_GT";
    Operator["NUM_GTE"] = "NUM_GTE";
    Operator["NUM_LT"] = "NUM_LT";
    Operator["NUM_LTE"] = "NUM_LTE";
    Operator["DATE_AFTER"] = "DATE_AFTER";
    Operator["DATE_BEFORE"] = "DATE_BEFORE";
    Operator["SEMVER_EQ"] = "SEMVER_EQ";
    Operator["SEMVER_GT"] = "SEMVER_GT";
    Operator["SEMVER_LT"] = "SEMVER_LT";
})(Operator || (exports.Operator = Operator = {}));
const cleanValues = (values) => values.filter((v) => !!v).map((v) => v.trim());
const isStrictSemver = (version) => (0, semver_1.clean)(version) === version;
const InOperator = (constraint, context) => {
    const field = constraint.contextName;
    const values = cleanValues(constraint.values);
    const contextValue = (0, helpers_1.resolveContextValue)(context, field);
    const isIn = values.some((val) => val === contextValue);
    return constraint.operator === Operator.IN ? isIn : !isIn;
};
const StringOperator = (constraint, context) => {
    const { contextName, operator, caseInsensitive } = constraint;
    let values = cleanValues(constraint.values);
    let contextValue = (0, helpers_1.resolveContextValue)(context, contextName);
    if (caseInsensitive) {
        values = values.map((v) => v.toLocaleLowerCase());
        contextValue = contextValue === null || contextValue === void 0 ? void 0 : contextValue.toLocaleLowerCase();
    }
    if (typeof contextValue !== 'string') {
        return false;
    }
    if (operator === Operator.STR_STARTS_WITH) {
        return values.some((val) => contextValue === null || contextValue === void 0 ? void 0 : contextValue.startsWith(val));
    }
    if (operator === Operator.STR_ENDS_WITH) {
        return values.some((val) => contextValue === null || contextValue === void 0 ? void 0 : contextValue.endsWith(val));
    }
    if (operator === Operator.STR_CONTAINS) {
        return values.some((val) => contextValue === null || contextValue === void 0 ? void 0 : contextValue.includes(val));
    }
    return false;
};
const SemverOperator = (constraint, context) => {
    const { contextName, operator } = constraint;
    const value = constraint.value;
    const contextValue = (0, helpers_1.resolveContextValue)(context, contextName);
    if (!contextValue) {
        return false;
    }
    try {
        if (!isStrictSemver(contextValue)) {
            return false;
        }
        if (operator === Operator.SEMVER_EQ) {
            return (0, semver_1.eq)(contextValue, value);
        }
        if (operator === Operator.SEMVER_LT) {
            return (0, semver_1.lt)(contextValue, value);
        }
        if (operator === Operator.SEMVER_GT) {
            return (0, semver_1.gt)(contextValue, value);
        }
    }
    catch (e) {
        return false;
    }
    return false;
};
const DateOperator = (constraint, context) => {
    const { operator } = constraint;
    const value = new Date(constraint.value);
    const currentTime = context.currentTime ? new Date(context.currentTime) : new Date();
    if (operator === Operator.DATE_AFTER) {
        return currentTime > value;
    }
    if (operator === Operator.DATE_BEFORE) {
        return currentTime < value;
    }
    return false;
};
const NumberOperator = (constraint, context) => {
    const field = constraint.contextName;
    const { operator } = constraint;
    const value = Number(constraint.value);
    const contextValue = Number((0, helpers_1.resolveContextValue)(context, field));
    if (Number.isNaN(value) || Number.isNaN(contextValue)) {
        return false;
    }
    if (operator === Operator.NUM_EQ) {
        return contextValue === value;
    }
    if (operator === Operator.NUM_GT) {
        return contextValue > value;
    }
    if (operator === Operator.NUM_GTE) {
        return contextValue >= value;
    }
    if (operator === Operator.NUM_LT) {
        return contextValue < value;
    }
    if (operator === Operator.NUM_LTE) {
        return contextValue <= value;
    }
    return false;
};
const operators = new Map();
operators.set(Operator.IN, InOperator);
operators.set(Operator.NOT_IN, InOperator);
operators.set(Operator.STR_STARTS_WITH, StringOperator);
operators.set(Operator.STR_ENDS_WITH, StringOperator);
operators.set(Operator.STR_CONTAINS, StringOperator);
operators.set(Operator.NUM_EQ, NumberOperator);
operators.set(Operator.NUM_LT, NumberOperator);
operators.set(Operator.NUM_LTE, NumberOperator);
operators.set(Operator.NUM_GT, NumberOperator);
operators.set(Operator.NUM_GTE, NumberOperator);
operators.set(Operator.DATE_AFTER, DateOperator);
operators.set(Operator.DATE_BEFORE, DateOperator);
operators.set(Operator.SEMVER_EQ, SemverOperator);
operators.set(Operator.SEMVER_GT, SemverOperator);
operators.set(Operator.SEMVER_LT, SemverOperator);
class Strategy {
    constructor(name, returnValue = false) {
        this.name = name || 'unknown';
        this.returnValue = returnValue;
    }
    checkConstraint(constraint, context) {
        const evaluator = operators.get(constraint.operator);
        if (!evaluator) {
            return false;
        }
        if (constraint.inverted) {
            return !evaluator(constraint, context);
        }
        return evaluator(constraint, context);
    }
    checkConstraints(context, constraints) {
        if (!constraints) {
            return true;
        }
        // eslint-disable-next-line no-restricted-syntax
        for (const constraint of constraints) {
            if (!constraint || !this.checkConstraint(constraint, context)) {
                return false;
            }
        }
        return true;
    }
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    isEnabled(parameters, context) {
        return this.returnValue;
    }
    isEnabledWithConstraints(parameters, context, constraints) {
        return this.checkConstraints(context, constraints) && this.isEnabled(parameters, context);
    }
    getResult(parameters, context, constraints, variants) {
        const enabled = this.isEnabledWithConstraints(parameters, context, constraints);
        if (enabled && Array.isArray(variants) && variants.length > 0) {
            const stickiness = variants[0].stickiness || parameters.stickiness;
            const variantDefinition = (0, variant_1.selectVariantDefinition)(parameters.groupId, stickiness, variants, context);
            return variantDefinition
                ? {
                    enabled: true,
                    variant: {
                        name: variantDefinition.name,
                        enabled: true,
                        payload: variantDefinition.payload,
                    },
                }
                : { enabled: true };
        }
        if (enabled) {
            return { enabled: true };
        }
        return { enabled: false };
    }
}
exports.Strategy = Strategy;//# sourceMappingURL=https://main.vscode-cdn.net/sourcemaps/146732d0c28fe6d8f982f1ebf7ab4101ece88c63/node_modules/unleash-client/lib/strategy/strategy.js.map