"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateAutoCompleteOptions = generateAutoCompleteOptions;
const grouping_1 = require("./grouping");
const operators_1 = require("./operators");
const query_builder_1 = require("./query-builder");
const text_matching_1 = require("./text-matching");
function generateAutoCompleteOptions(queryState, filteringProperties = [], filteringOptions = []) {
    var _a;
    /* State: Property and operator are matched, suggest values */
    if (queryState.step === "property") {
        const filterText = queryState.value || "";
        return {
            value: queryState.value,
            options: createValueSuggestions(filteringOptions, queryState.operator, filterText, queryState.property),
        };
    }
    /* State: Property matched, but operator is incomplete */
    if (queryState.step === "operator") {
        const operators = filterOperatorsByPrefix(getValidOperatorsForProperty(queryState.property), queryState.operatorPrefix);
        const partialQuery = (0, query_builder_1.buildQueryString)(queryState.property.label, queryState.operatorPrefix, "");
        /**
         * Edge case: User typed an invalid operator prefix that doesn't match any operators.
         * This can happen when typing characters that don't start any valid operator.
         * Return empty suggestions gracefully - the UI will show "no results".
         */
        if (operators.length === 0) {
            return {
                value: partialQuery,
                options: [],
            };
        }
        return {
            value: partialQuery,
            options: generateOperatorSuggestions(queryState.property, queryState.operatorPrefix),
        };
    }
    /*
     * Edge case: Input starts with operator but has no value yet (user typed just "!=")
     * Wait for value before showing suggestions
     */
    if (!queryState.value && queryState.operator) {
        return {
            value: "",
            options: [],
        };
    }
    /* Empty input: Show all properties */
    if (!queryState.value) {
        return {
            value: "",
            options: generatePropertySuggestions(filteringProperties),
        };
    }
    /*
     * Free-text search: Show matching values across all properties
     * Use the detected operator if input started with one (e.g., "!= test"), otherwise default to "="
     */
    return {
        value: queryState.value,
        options: [
            ...generatePropertySuggestions(filteringProperties, queryState.value),
            ...createValueSuggestions(filteringOptions, (_a = queryState.operator) !== null && _a !== void 0 ? _a : "=", queryState.value),
        ],
    };
}
/**
 * Returns the valid operators for a given property.
 * Extracts operators from the property's custom operator configuration.
 * If none are configured, falls back to all available operators.
 *
 * The QueryFilteringScopedOperator can be a simple string (e.g., "=")
 * or an object with operator and tokenType (e.g., { operator: ":", tokenType: "single" }).
 * This function normalizes both formats and returns just the operator strings.
 *
 * @returns Array of valid operators for the property
 *
 * TODO: We omit passing the tokenType for now since it's not currently used in the UI. But will be needed for single/multi-selection.
 */
function getValidOperatorsForProperty(property) {
    const { operators } = property;
    /* If no operators configured, return all available operators */
    if (!operators || operators.length === 0) {
        return operators_1.QUERY_OPERATORS;
    }
    /*
     * Extract operator strings from QueryFilteringScopedOperator format
     * Handle both simple strings and objects with operator property
     */
    const operatorStrings = operators.map((op) => typeof op === "string" ? op : op.operator);
    /* Filter to only valid QUERY_OPERATORS to ensure type safety */
    return operatorStrings.filter((op) => operators_1.QUERY_OPERATORS.includes(op));
}
/**
 * Filters the list of operators based on the provided prefix.
 * If the prefix is empty, all operators are returned.
 */
function filterOperatorsByPrefix(operators, prefix) {
    if (!prefix) {
        return operators;
    }
    return operators.filter((operator) => operator.startsWith(prefix));
}
function generatePropertySuggestions(filteringProperties = [], filterText = "") {
    const filteredProperties = [];
    for (const property of filteringProperties) {
        if (!property) {
            continue;
        }
        if ((0, text_matching_1.matchesFilterText)([property.label, property.groupLabel, property.group].filter(Boolean), filterText)) {
            filteredProperties.push(property);
        }
    }
    const groups = (0, grouping_1.createGroups)(filteredProperties, (property) => property.group, "Properties");
    return groups.map((group) => ({
        label: group.label,
        options: group.options.map((property) => ({
            value: (0, query_builder_1.buildQueryString)(property.label, "", ""),
            label: property.label,
        })),
    }));
}
function generateOperatorSuggestions(property, operatorPrefix = "") {
    const operators = filterOperatorsByPrefix(getValidOperatorsForProperty(property), operatorPrefix);
    if (operators.length === 0) {
        return [];
    }
    return [
        {
            label: "Operators",
            options: operators.map((operator) => {
                var _a;
                return ({
                    value: (0, query_builder_1.buildQueryString)(property.label, operator, ""),
                    label: (0, query_builder_1.buildQueryString)(property.label, operator, ""),
                    description: (_a = query_builder_1.OPERATOR_LABELS[operator]) !== null && _a !== void 0 ? _a : "",
                });
            }),
        },
    ];
}
/**
 * Creates value suggestions for autocomplete.
 * When scopedProperty is provided, only shows values for that property (single group).
 * When scopedProperty is omitted, searches across all properties (multiple groups).
 * TODO: This could potentially contain an unlimited number of options if there are many values across properties.
 * May need virtualization/async or other filtering mechanism.
 */
function createValueSuggestions(filteringOptions = [], operator, filterText = "", scopedProperty) {
    var _a;
    const groups = {};
    for (const option of filteringOptions) {
        if (!(option === null || option === void 0 ? void 0 : option.property)) {
            continue;
        }
        /* If scoped to a property, filter to only that property's options */
        if (scopedProperty && option.property !== scopedProperty) {
            continue;
        }
        /* Build search fields */
        const searchFields = [option.label, ...((_a = option.tags) !== null && _a !== void 0 ? _a : [])];
        if (!scopedProperty) {
            searchFields.push(option.property.label);
        }
        const matches = (0, text_matching_1.matchesFilterText)(searchFields.filter(Boolean), filterText);
        if (!matches) {
            continue;
        }
        const groupLabel = option.property.groupLabel || "Values";
        if (!groups[groupLabel]) {
            groups[groupLabel] = {
                label: groupLabel,
                options: [],
            };
        }
        groups[groupLabel].options.push({
            value: (0, query_builder_1.buildQueryString)(option.property.label, operator, option.value),
            label: (0, query_builder_1.buildQueryString)(option.property.label, operator, option.value),
            tags: option.tags,
        });
    }
    return Object.values(groups).filter((group) => group.options.length > 0);
}
//# sourceMappingURL=generate-autocomplete-options.js.map