"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getFields = exports.getMetricsWithValidParameters = exports.getMetricsMapFromTables = exports.getMetrics = exports.getDimensionsWithValidParameters = exports.getDimensionMapFromTables = exports.getDimensions = void 0;
exports.compareMetricAndCustomMetric = compareMetricAndCustomMetric;
exports.findReplaceableCustomMetrics = findReplaceableCustomMetrics;
exports.convertReplaceableFieldMatchMapToReplaceFieldsMap = convertReplaceableFieldMatchMapToReplaceFieldsMap;
exports.convertReplaceableFieldMatchMapToReplaceCustomFields = convertReplaceableFieldMatchMapToReplaceCustomFields;
const filter_1 = require("../types/filter");
const formatting_1 = require("./formatting");
const item_1 = require("./item");
// Helper function to get a list of all tables in an explore with valid parameters
const getTablesWithValidParameters = (explore, combinedParameters) => {
    const joinedTablesNames = explore.joinedTables.map((j) => j.table);
    const joinedTablesWithValidParameters = explore.joinedTables.reduce((acc, join) => {
        const joinHasValidParameters = join.parameterReferences?.every((paramRef) => combinedParameters[paramRef]) ?? true;
        if (joinHasValidParameters) {
            acc.push(join.table);
        }
        return acc;
    }, []);
    const tablesWithValidParameters = Object.values(explore.tables).filter((table) => {
        const tableHasValidParameters = table.parameterReferences?.every((paramRef) => combinedParameters[paramRef]) ?? true;
        const tableIsJoined = joinedTablesNames.includes(table.name);
        const tableIsJoinedWithValidParameters = joinedTablesWithValidParameters.includes(table.name);
        if (tableIsJoined && !tableIsJoinedWithValidParameters) {
            return false;
        }
        return tableHasValidParameters;
    });
    return tablesWithValidParameters;
};
// Helper function to get a list of all dimensions in an explore
/**
 * @deprecated Use `getDimensionMapFromTables` instead
 */
const getDimensions = (explore) => Object.values(explore.tables).flatMap((t) => Object.values(t.dimensions));
exports.getDimensions = getDimensions;
const getDimensionMapFromTables = (tables) => Object.values(tables).reduce((acc, table) => {
    Object.values(table.dimensions).forEach((dimension) => {
        acc[(0, item_1.getItemId)(dimension)] = dimension;
    });
    return acc;
}, {});
exports.getDimensionMapFromTables = getDimensionMapFromTables;
const getDimensionsWithValidParameters = (explore, combinedParameters) => {
    function dimensionHasAllValidParameters(dimension) {
        return (dimension.parameterReferences?.every((p) => combinedParameters[p]) ?? true);
    }
    const tablesWithValidParameters = getTablesWithValidParameters(explore, combinedParameters);
    // Dimensions with valid parameters in the base table
    const baseTableDimensions = tablesWithValidParameters
        .filter((t) => t.name === explore.baseTable)
        .flatMap((t) => Object.values(t.dimensions).filter(dimensionHasAllValidParameters));
    // Dimensions with valid parameters in the joined tables
    const joinedTableDimensions = tablesWithValidParameters
        .filter((t) => t.name !== explore.baseTable)
        .flatMap((t) => Object.values(t.dimensions).filter(dimensionHasAllValidParameters));
    // Prioritize base table dimensions over joined table dimensions
    return [...baseTableDimensions, ...joinedTableDimensions];
};
exports.getDimensionsWithValidParameters = getDimensionsWithValidParameters;
// Helper function to get a list of all metrics in an explore
// @deprecated Use `getMetricsMapFromTables` instead
const getMetrics = (explore) => Object.values(explore.tables).flatMap((t) => Object.values(t.metrics));
exports.getMetrics = getMetrics;
const getMetricsMapFromTables = (tables) => Object.values(tables).reduce((acc, table) => {
    Object.values(table.metrics).forEach((metric) => {
        acc[(0, item_1.getItemId)(metric)] = metric;
    });
    return acc;
}, {});
exports.getMetricsMapFromTables = getMetricsMapFromTables;
const getMetricsWithValidParameters = (explore, combinedParameters) => getTablesWithValidParameters(explore, combinedParameters).flatMap((t) => Object.values(t.metrics).filter((m) => m.parameterReferences?.every((p) => combinedParameters[p]) ??
    true));
exports.getMetricsWithValidParameters = getMetricsWithValidParameters;
const getFields = (explore) => [
    ...(0, exports.getDimensions)(explore),
    ...(0, exports.getMetrics)(explore),
];
exports.getFields = getFields;
function compareMetricAndCustomMetric({ metric, customMetric, }) {
    const conditions = {
        fieldIdMatch: {
            isMatch: (0, item_1.getItemId)(metric) === (0, item_1.getItemId)(customMetric),
            requiredForSuggestion: false,
        },
        labelMatch: {
            isMatch: metric.label === customMetric.label,
            requiredForSuggestion: false,
        },
        sqlMatch: {
            isMatch: metric.sql === customMetric.sql,
            requiredForSuggestion: true,
        },
        baseDimensionMatch: {
            isMatch: metric.dimensionReference ===
                `${customMetric.table}_${customMetric.baseDimensionName}`,
            requiredForSuggestion: true,
        },
        metricTypeMatch: {
            isMatch: metric.type === customMetric.type,
            requiredForSuggestion: true,
        },
        formatMatch: {
            isMatch: (0, formatting_1.getFormatExpression)(metric) ===
                (0, formatting_1.getFormatExpression)(customMetric),
            requiredForSuggestion: true,
        },
        percentileMatch: {
            isMatch: metric.percentile === customMetric.percentile,
            requiredForSuggestion: true,
        },
        filtersMatch: {
            isMatch: (customMetric.filters || []).length ===
                (metric.filters || []).length &&
                (metric.filters || []).every((filter) => customMetric.filters?.find((customFilter) => {
                    const fieldRefMatch = customFilter.target.fieldRef ===
                        filter.target.fieldRef;
                    const operatorMatch = customFilter.operator === filter.operator;
                    const valuesMatch = customFilter.values === filter.values ||
                        customFilter.values?.every((value) => filter.values?.includes(value));
                    let settingsMatch = (0, filter_1.isDateFilterRule)(customFilter) ===
                        (0, filter_1.isDateFilterRule)(filter);
                    if ((0, filter_1.isDateFilterRule)(customFilter) &&
                        (0, filter_1.isDateFilterRule)(filter)) {
                        const metricSettings = filter.settings;
                        const customMetricSettings = customFilter.settings;
                        settingsMatch =
                            metricSettings.unitOfTime ===
                                customMetricSettings.unitOfTime;
                    }
                    return (fieldRefMatch &&
                        operatorMatch &&
                        valuesMatch &&
                        settingsMatch);
                })),
            requiredForSuggestion: true,
        },
    };
    const isExactMatch = Object.values(conditions).every((condition) => condition.isMatch);
    const isSuggestedMatch = Object.values(conditions)
        .filter((condition) => condition.requiredForSuggestion)
        .every((condition) => condition.isMatch);
    return {
        isExactMatch,
        isSuggestedMatch,
    };
}
function findReplaceableCustomMetrics({ customMetrics, metrics, }) {
    return customMetrics.reduce((acc, customMetric) => {
        let match = null;
        const suggestedMatches = [];
        metrics.forEach((metric) => {
            const fieldId = (0, item_1.getItemId)(metric);
            const fieldLabel = metric.label;
            const { isExactMatch, isSuggestedMatch } = compareMetricAndCustomMetric({
                metric,
                customMetric,
            });
            if (isExactMatch) {
                match = {
                    fieldId,
                    fieldLabel,
                };
            }
            else if (isSuggestedMatch) {
                suggestedMatches.push({
                    fieldId,
                    fieldLabel,
                });
            }
        });
        if (match !== null || suggestedMatches.length > 0) {
            return {
                ...acc,
                [(0, item_1.getItemId)(customMetric)]: {
                    fieldId: customMetric.name,
                    label: customMetric.label || customMetric.name,
                    match,
                    suggestedMatches,
                },
            };
        }
        return acc;
    }, {});
}
function convertReplaceableFieldMatchMapToReplaceFieldsMap(replaceableFieldMap) {
    return Object.entries(replaceableFieldMap).reduce((acc2, [customFieldId, customField]) => {
        if (customField.match) {
            return {
                ...acc2,
                [customFieldId]: {
                    replaceWithFieldId: customField.match.fieldId,
                },
            };
        }
        return acc2;
    }, {});
}
// todo: rename+breakdown this util and all the replace/replaceable types to be more descriptive
function convertReplaceableFieldMatchMapToReplaceCustomFields(replaceableCustomFields) {
    return Object.entries(replaceableCustomFields).reduce((acc, [chartUuid, customFields]) => {
        const customMetrics = convertReplaceableFieldMatchMapToReplaceFieldsMap(customFields.customMetrics);
        if (Object.keys(customMetrics).length > 0) {
            acc[chartUuid] = {
                customMetrics,
            };
        }
        return acc;
    }, {});
}
//# sourceMappingURL=fields.js.map