"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateCollectionFunctionScore = exports.generateSearchFunctionScore = exports.generateQueryCollectionWeighted = exports.generateQuerySearchWeighted = void 0;
const firestore_1 = require("../types/firestore");
const generateQuerySearchWeighted = (options) => {
    var _a, _b;
    return (0, exports.generateSearchFunctionScore)({
        searchTerm: (_a = options.curation.searchTerm) !== null && _a !== void 0 ? _a : "",
        searchableFields: options.searchableFields,
        boostingRules: (_b = options.curation.boostings) !== null && _b !== void 0 ? _b : [],
        markets: options.curation.markets,
        market: options.market,
    });
};
exports.generateQuerySearchWeighted = generateQuerySearchWeighted;
const generateQueryCollectionWeighted = (options) => {
    var _a, _b;
    const collectionFunctionScore = (0, exports.generateCollectionFunctionScore)({
        collectionHandle: (_a = options.curation.collectionHandle) !== null && _a !== void 0 ? _a : "",
        boostingRules: (_b = options.curation.boostings) !== null && _b !== void 0 ? _b : [],
        markets: options.curation.markets,
        market: options.market,
    });
    return { function_score: collectionFunctionScore };
};
exports.generateQueryCollectionWeighted = generateQueryCollectionWeighted;
const generateMarketsClause = (markets) => {
    if (!markets.length)
        return null;
    return { terms: { markets: markets } };
};
const generateQueryField = (options) => {
    if (!options.market) {
        if (options.fieldName === "price")
            return "price_min";
        return options.fieldName;
    }
    if (options.fieldName === "price") {
        return `price_min_market_${options.market}`;
    }
    if (options.fieldName === "price_min") {
        return `price_min_market_${options.market}`;
    }
    if (options.fieldName === "price_max") {
        return `price_max_market_${options.market}`;
    }
    if (options.fieldName === "inventory_total") {
        return `inventory_quantity_market_${options.market}`;
    }
    return options.fieldName;
};
const generateBoostingRule = (options) => {
    const fieldName = generateQueryField({
        fieldName: options.boostingRule.fieldName,
        market: options.market,
    });
    if (options.boostingRule.operation === firestore_1.Operation.Contains) {
        return {
            filter: { wildcard: { [fieldName]: `*${options.boostingRule.value}*` } },
            weight: options.boostingRule.points,
        };
    }
    if (options.boostingRule.operation === firestore_1.Operation.NotContains) {
        return {
            filter: {
                bool: { must_not: { wildcard: { [fieldName]: `*${options.boostingRule.value}*` } } },
            },
            weight: options.boostingRule.points,
        };
    }
    if (options.boostingRule.operation === firestore_1.Operation.Equals) {
        return {
            filter: { term: { [fieldName]: options.boostingRule.value } },
            weight: options.boostingRule.points,
        };
    }
    if (options.boostingRule.operation === firestore_1.Operation.GreaterThan) {
        return {
            filter: { range: { [fieldName]: { gt: parseFloat(options.boostingRule.value) } } },
            weight: options.boostingRule.points,
        };
    }
    if (options.boostingRule.operation === firestore_1.Operation.LessThan) {
        return {
            filter: { range: { [fieldName]: { lt: parseFloat(options.boostingRule.value) } } },
            weight: options.boostingRule.points,
        };
    }
    throw new Error(`Unable to process rule`);
};
const generateBoostingRules = (options) => {
    return options.boostingRules
        .map((boostingRule) => generateBoostingRule({ boostingRule: boostingRule, market: options.market }))
        .filter(Boolean);
};
const generateSearchFunctionScore = (options) => {
    var _a, _b;
    const searchTerms = (_b = (_a = options.searchTerm) === null || _a === void 0 ? void 0 : _a.split(" ")) !== null && _b !== void 0 ? _b : [];
    const searchQueries = [];
    for (const searchableField of options.searchableFields) {
        const { allowedMistakes, field } = searchableField;
        const baseField = field.replace(/\.[^.]*$/, "");
        for (const searchTerm of searchTerms) {
            searchQueries.push({
                match: {
                    [baseField]: { query: searchTerm, fuzziness: allowedMistakes, analyzer: "regular_analyzer" },
                },
            });
            if (searchableField.synyonymEnabled) {
                searchQueries.push({
                    match: {
                        [baseField]: { query: searchTerm, fuzziness: allowedMistakes, analyzer: "synonym_analyzer" },
                    },
                });
            }
        }
    }
    const searchScores = [];
    for (const searchableField of options.searchableFields) {
        const { field, points, allowedMistakes, mistakePenaltyPercent, synyonymEnabled, synonymPenaltyPercent } = searchableField;
        const baseField = field.replace(/\.[^.]*$/, "");
        const mistakeWeight = (points * mistakePenaltyPercent) / 100;
        const baseWeight = points - allowedMistakes * mistakeWeight;
        const synonymWeight = (points * synonymPenaltyPercent) / 100;
        const synonymBaseWeight = points - allowedMistakes * synonymWeight;
        for (let edits = 0; edits <= allowedMistakes; edits++) {
            const weight = edits === 0 ? baseWeight : mistakeWeight;
            for (const searchTerm of searchTerms) {
                // Regular match
                searchScores.push({
                    filter: {
                        match: { [baseField]: { query: searchTerm, fuzziness: edits, analyzer: "regular_analyzer" } },
                    },
                    weight: weight,
                });
                // Synonym match _and not_ regular match
                if (synyonymEnabled && searchableField.synonymPenaltyPercent > 0) {
                    const weight = edits === 0 ? synonymBaseWeight : synonymWeight;
                    searchScores.push({
                        filter: {
                            bool: {
                                must_not: {
                                    match: { [baseField]: { query: searchTerm, fuzziness: edits, analyzer: "regular_analyzer" } },
                                },
                                should: {
                                    match: { [baseField]: { query: searchTerm, fuzziness: edits, analyzer: "synonym_analyzer" } },
                                },
                            },
                        },
                        weight: weight,
                    });
                }
            }
        }
    }
    return {
        query: { bool: Object.assign(Object.assign({}, generateMarketsClause(options.markets)), { should: [...searchQueries] }) },
        functions: [
            ...searchScores,
            ...generateBoostingRules({
                boostingRules: options.boostingRules,
                market: options.market,
            }),
        ],
        score_mode: "sum",
        boost_mode: "replace",
    };
};
exports.generateSearchFunctionScore = generateSearchFunctionScore;
const generateCollectionFunctionScore = (options) => {
    const mustClauses = [
        {
            nested: {
                path: "collections",
                query: {
                    term: { ["collections.handle"]: options.collectionHandle },
                },
            },
        },
    ];
    const marketClause = generateMarketsClause(options.markets);
    if (marketClause)
        mustClauses.push(marketClause);
    return {
        query: {
            bool: { must: mustClauses },
        },
        functions: generateBoostingRules({
            boostingRules: options.boostingRules,
            market: options.market,
        }),
        score_mode: "sum",
        boost_mode: "replace",
    };
};
exports.generateCollectionFunctionScore = generateCollectionFunctionScore;
//# sourceMappingURL=queries.js.map