"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseModelRequiredFilters = exports.parseFilters = exports.parseOperator = void 0;
const tslib_1 = require("tslib");
const uuid_1 = require("uuid");
const errors_1 = require("./errors");
const filter_1 = require("./filter");
const filterGrammar_grammar_1 = tslib_1.__importDefault(require("./filterGrammar.grammar"));
// Precompiled, eval-free parser generated from `filterGrammar.grammar.ts`. Using
// it instead of `peg.generate(filterGrammar)` keeps the browser SDK bundle free
// of `eval()`/`new Function()` so it works under a strict CSP. See issue #21276.
const filterGrammar_parser_1 = require("./filterGrammar.parser");
const parseOperator = (operator, isTrue, fieldName) => {
    switch (operator) {
        case filter_1.FilterOperator.EQUALS:
            return isTrue ? filter_1.FilterOperator.EQUALS : filter_1.FilterOperator.NOT_EQUALS;
        case filter_1.FilterOperator.INCLUDE:
            return isTrue ? filter_1.FilterOperator.INCLUDE : filter_1.FilterOperator.NOT_INCLUDE;
        case filter_1.FilterOperator.STARTS_WITH:
            return filter_1.FilterOperator.STARTS_WITH;
        case filter_1.FilterOperator.ENDS_WITH:
            return filter_1.FilterOperator.ENDS_WITH;
        case '>':
            return filter_1.FilterOperator.GREATER_THAN;
        case '>=':
            return filter_1.FilterOperator.GREATER_THAN_OR_EQUAL;
        case '<':
            return filter_1.FilterOperator.LESS_THAN;
        case '<=':
            return filter_1.FilterOperator.LESS_THAN_OR_EQUAL;
        case filter_1.FilterOperator.IN_THE_PAST:
            return filter_1.FilterOperator.IN_THE_PAST;
        case filter_1.FilterOperator.IN_THE_NEXT:
            return filter_1.FilterOperator.IN_THE_NEXT;
        case filter_1.FilterOperator.IN_BETWEEN:
            return isTrue
                ? filter_1.FilterOperator.IN_BETWEEN
                : filter_1.FilterOperator.NOT_IN_BETWEEN;
        case 'null':
        case 'NULL':
            return isTrue ? filter_1.FilterOperator.NULL : filter_1.FilterOperator.NOT_NULL;
        default:
            throw new errors_1.UnexpectedServerError(`${fieldName} uses invalid filter operator type ${operator}`);
    }
};
exports.parseOperator = parseOperator;
const parseFilters = (rawFilters) => {
    if (!rawFilters || rawFilters.length === 0) {
        return [];
    }
    const parser = filterGrammar_parser_1.filterGrammarParser;
    return rawFilters.reduce((acc, filter) => {
        if (Object.entries(filter).length !== 1)
            return acc;
        const [key, value] = Object.entries(filter)[0];
        if (value === null) {
            return [
                ...acc,
                {
                    id: (0, uuid_1.v4)(),
                    target: { fieldRef: key },
                    operator: filter_1.FilterOperator.NULL,
                    values: [1],
                },
            ];
        }
        if (typeof value === 'string') {
            const parsedFilter = parser.parse(value);
            return [
                ...acc,
                {
                    id: (0, uuid_1.v4)(),
                    target: { fieldRef: key },
                    operator: (0, exports.parseOperator)(parsedFilter.type, !!parsedFilter.is, key),
                    values: parsedFilter.values || [1],
                    ...(parsedFilter.date_interval
                        ? {
                            settings: {
                                unitOfTime: parsedFilter.date_interval,
                            },
                        }
                        : null),
                },
            ];
        }
        if (typeof value === 'object') {
            return [
                ...acc,
                {
                    id: (0, uuid_1.v4)(),
                    target: { fieldRef: key },
                    operator: filter_1.FilterOperator.EQUALS,
                    values: value,
                },
            ];
        }
        return [
            ...acc,
            {
                id: (0, uuid_1.v4)(),
                target: { fieldRef: key },
                operator: filter_1.FilterOperator.EQUALS,
                values: [value],
            },
        ];
    }, []);
};
exports.parseFilters = parseFilters;
const parseModelRequiredFilters = ({ requiredFilters, defaultFilters, }) => {
    const rawFilters = [...(requiredFilters || []), ...(defaultFilters || [])];
    if (!rawFilters || rawFilters.length === 0) {
        return [];
    }
    const parser = filterGrammar_parser_1.filterGrammarParser;
    return rawFilters.reduce((acc, filter) => {
        const parseFilter = () => {
            const requiredDefault = requiredFilters?.includes(filter) ?? false;
            const filterHasMultipleKeys = Object.keys(filter).length > 1;
            if (filterHasMultipleKeys) {
                // Require is a special property that is not part of the filter grammar
                const { required, ...filterRule } = filter; // Remove require from object
                // we default to true for backwards compatibility
                return [
                    required === undefined ? requiredDefault : required,
                    filterRule,
                ];
            }
            // If there is only one key, we still want to return it as it is.
            // This is to cover the case where the filter is just { required: true }, when required is used as a field.
            // We currently don't support a "required" field which is "not" required (as in: required: false), because those keys will clash
            return [requiredDefault, filter];
        };
        const [required, filterRule] = parseFilter();
        if (Object.entries(filterRule).length !== 1)
            return acc;
        const [key, value] = Object.entries(filterRule)[0];
        if (acc.map((a) => a.target.fieldRef).includes(key)) {
            // eslint-disable-next-line no-console
            console.warn(`Duplicate filter key "${key}" in default filters`);
            return acc;
        }
        const fieldRefParts = key.split('.');
        const filterTarget = fieldRefParts.length !== 2
            ? { fieldRef: key }
            : { fieldRef: key, tableName: fieldRefParts[0] };
        if (value === null) {
            return [
                ...acc,
                {
                    id: (0, uuid_1.v4)(),
                    target: filterTarget,
                    operator: filter_1.FilterOperator.NULL,
                    values: [1],
                    required,
                },
            ];
        }
        if (typeof value === 'string') {
            const parsedFilter = parser.parse(value);
            return [
                ...acc,
                {
                    id: (0, uuid_1.v4)(),
                    target: filterTarget,
                    operator: (0, exports.parseOperator)(parsedFilter.type, !!parsedFilter.is, key),
                    values: parsedFilter.values || [1],
                    ...(parsedFilter.date_interval
                        ? {
                            settings: {
                                unitOfTime: parsedFilter.date_interval,
                            },
                        }
                        : null),
                    required,
                },
            ];
        }
        if (typeof value === 'object') {
            return [
                ...acc,
                {
                    id: (0, uuid_1.v4)(),
                    target: filterTarget,
                    operator: filter_1.FilterOperator.EQUALS,
                    values: value,
                    required,
                },
            ];
        }
        return [
            ...acc,
            {
                id: (0, uuid_1.v4)(),
                target: filterTarget,
                operator: filter_1.FilterOperator.EQUALS,
                values: [value],
                required,
            },
        ];
    }, []);
};
exports.parseModelRequiredFilters = parseModelRequiredFilters;
// Re-exported for backward compatibility (e.g. tests that compile the grammar
// directly); the runtime path uses the precompiled `filterGrammarParser` above.
exports.default = filterGrammar_grammar_1.default;
//# sourceMappingURL=filterGrammar.js.map