"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.analyzePreAggregateNumberMetricDependencies = exports.PreAggregateNumberMetricDependencyIneligibilityReason = void 0;
const exploreCompiler_1 = require("../../compiler/exploreCompiler");
const field_1 = require("../../types/field");
const item_1 = require("../../utils/item");
const metricRepresentation_1 = require("./metricRepresentation");
const referenceLookup_1 = require("./referenceLookup");
var PreAggregateNumberMetricDependencyIneligibilityReason;
(function (PreAggregateNumberMetricDependencyIneligibilityReason) {
    PreAggregateNumberMetricDependencyIneligibilityReason["CIRCULAR_DEPENDENCY"] = "circular_dependency";
    PreAggregateNumberMetricDependencyIneligibilityReason["NESTED_AGGREGATE_METRIC_REFERENCE"] = "nested_aggregate_metric_reference";
    PreAggregateNumberMetricDependencyIneligibilityReason["NO_METRIC_REFERENCES"] = "no_metric_references";
    PreAggregateNumberMetricDependencyIneligibilityReason["UNSUPPORTED_LEAF_METRIC_TYPE"] = "unsupported_leaf_metric_type";
})(PreAggregateNumberMetricDependencyIneligibilityReason || (exports.PreAggregateNumberMetricDependencyIneligibilityReason = PreAggregateNumberMetricDependencyIneligibilityReason = {}));
const mergeFieldIds = (current, next) => Array.from(new Set([...current, ...next]));
const getIneligibleResult = ({ reason, ineligibleMetricFieldId, directReferencedMetricFieldIds, transitiveReferencedMetricFieldIds, leafMetricFieldIds, }) => ({
    isValid: false,
    reason,
    ineligibleMetricFieldId,
    directReferencedMetricFieldIds,
    transitiveReferencedMetricFieldIds,
    leafMetricFieldIds,
});
const analyzeNumberMetricDependencies = ({ metric, tables, state, }) => {
    const currentFieldId = (0, item_1.getItemId)(metric);
    const cachedResult = state.cache.get(currentFieldId);
    if (cachedResult) {
        return cachedResult;
    }
    if (state.activeMetricFieldIds.has(currentFieldId)) {
        return getIneligibleResult({
            reason: PreAggregateNumberMetricDependencyIneligibilityReason.CIRCULAR_DEPENDENCY,
            ineligibleMetricFieldId: currentFieldId,
            directReferencedMetricFieldIds: [],
            transitiveReferencedMetricFieldIds: [],
            leafMetricFieldIds: [],
        });
    }
    state.activeMetricFieldIds.add(currentFieldId);
    let result = {
        isValid: true,
        directReferencedMetricFieldIds: [],
        transitiveReferencedMetricFieldIds: [],
        leafMetricFieldIds: [],
    };
    try {
        const directMetricReferences = (0, exploreCompiler_1.getAllReferences)(metric.sql)
            .map((ref) => ({
            ref,
            referencedMetric: (0, referenceLookup_1.getReferencedMetricForPreAggregation)({
                metric,
                ref,
                tables,
            }),
        }))
            .filter((candidate) => candidate.referencedMetric !== undefined);
        if (directMetricReferences.length === 0) {
            result = getIneligibleResult({
                reason: PreAggregateNumberMetricDependencyIneligibilityReason.NO_METRIC_REFERENCES,
                ineligibleMetricFieldId: currentFieldId,
                directReferencedMetricFieldIds: [],
                transitiveReferencedMetricFieldIds: [],
                leafMetricFieldIds: [],
            });
            return result;
        }
        if ((0, exploreCompiler_1.sqlAggregationWrapsReferences)(metric.sql, directMetricReferences.map(({ ref }) => ref))) {
            result = getIneligibleResult({
                reason: PreAggregateNumberMetricDependencyIneligibilityReason.NESTED_AGGREGATE_METRIC_REFERENCE,
                ineligibleMetricFieldId: currentFieldId,
                directReferencedMetricFieldIds: directMetricReferences.map(({ referencedMetric }) => (0, item_1.getItemId)(referencedMetric)),
                transitiveReferencedMetricFieldIds: directMetricReferences.map(({ referencedMetric }) => (0, item_1.getItemId)(referencedMetric)),
                leafMetricFieldIds: [],
            });
            return result;
        }
        let directReferencedMetricFieldIds = [];
        let transitiveReferencedMetricFieldIds = [];
        let leafMetricFieldIds = [];
        for (const { referencedMetric } of directMetricReferences) {
            const referencedMetricFieldId = (0, item_1.getItemId)(referencedMetric);
            directReferencedMetricFieldIds = mergeFieldIds(directReferencedMetricFieldIds, [referencedMetricFieldId]);
            transitiveReferencedMetricFieldIds = mergeFieldIds(transitiveReferencedMetricFieldIds, [referencedMetricFieldId]);
            if (referencedMetric.type === field_1.MetricType.NUMBER) {
                const nestedResult = analyzeNumberMetricDependencies({
                    metric: referencedMetric,
                    tables,
                    state,
                });
                transitiveReferencedMetricFieldIds = mergeFieldIds(transitiveReferencedMetricFieldIds, nestedResult.transitiveReferencedMetricFieldIds);
                leafMetricFieldIds = mergeFieldIds(leafMetricFieldIds, nestedResult.leafMetricFieldIds);
                if (!nestedResult.isValid) {
                    result = getIneligibleResult({
                        reason: nestedResult.reason,
                        ineligibleMetricFieldId: nestedResult.ineligibleMetricFieldId,
                        directReferencedMetricFieldIds,
                        transitiveReferencedMetricFieldIds,
                        leafMetricFieldIds,
                    });
                    return result;
                }
                // eslint-disable-next-line no-continue
                continue;
            }
            if (!(0, metricRepresentation_1.isSupportedMetricType)(referencedMetric.type)) {
                result = getIneligibleResult({
                    reason: PreAggregateNumberMetricDependencyIneligibilityReason.UNSUPPORTED_LEAF_METRIC_TYPE,
                    ineligibleMetricFieldId: referencedMetricFieldId,
                    directReferencedMetricFieldIds,
                    transitiveReferencedMetricFieldIds,
                    leafMetricFieldIds,
                });
                return result;
            }
            leafMetricFieldIds = mergeFieldIds(leafMetricFieldIds, [
                referencedMetricFieldId,
            ]);
        }
        result = {
            isValid: true,
            directReferencedMetricFieldIds,
            transitiveReferencedMetricFieldIds,
            leafMetricFieldIds,
        };
        return result;
    }
    finally {
        state.activeMetricFieldIds.delete(currentFieldId);
        state.cache.set(currentFieldId, result);
    }
};
const analyzePreAggregateNumberMetricDependencies = ({ metric, tables, }) => analyzeNumberMetricDependencies({
    metric,
    tables,
    state: {
        activeMetricFieldIds: new Set(),
        cache: new Map(),
    },
});
exports.analyzePreAggregateNumberMetricDependencies = analyzePreAggregateNumberMetricDependencies;
//# sourceMappingURL=numberMetricDependencies.js.map