import { getAllReferences, sqlAggregationWrapsReferences, } from '../../compiler/exploreCompiler';
import { MetricType, } from '../../types/field';
import { getItemId } from '../../utils/item';
import { isSupportedMetricType } from './metricRepresentation';
import { getReferencedMetricForPreAggregation, } from './referenceLookup';
export 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 || (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 = 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 = getAllReferences(metric.sql)
            .map((ref) => ({
            ref,
            referencedMetric: 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 (sqlAggregationWrapsReferences(metric.sql, directMetricReferences.map(({ ref }) => ref))) {
            result = getIneligibleResult({
                reason: PreAggregateNumberMetricDependencyIneligibilityReason.NESTED_AGGREGATE_METRIC_REFERENCE,
                ineligibleMetricFieldId: currentFieldId,
                directReferencedMetricFieldIds: directMetricReferences.map(({ referencedMetric }) => getItemId(referencedMetric)),
                transitiveReferencedMetricFieldIds: directMetricReferences.map(({ referencedMetric }) => getItemId(referencedMetric)),
                leafMetricFieldIds: [],
            });
            return result;
        }
        let directReferencedMetricFieldIds = [];
        let transitiveReferencedMetricFieldIds = [];
        let leafMetricFieldIds = [];
        for (const { referencedMetric } of directMetricReferences) {
            const referencedMetricFieldId = getItemId(referencedMetric);
            directReferencedMetricFieldIds = mergeFieldIds(directReferencedMetricFieldIds, [referencedMetricFieldId]);
            transitiveReferencedMetricFieldIds = mergeFieldIds(transitiveReferencedMetricFieldIds, [referencedMetricFieldId]);
            if (referencedMetric.type === 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 (!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);
    }
};
export const analyzePreAggregateNumberMetricDependencies = ({ metric, tables, }) => analyzeNumberMetricDependencies({
    metric,
    tables,
    state: {
        activeMetricFieldIds: new Set(),
        cache: new Map(),
    },
});
//# sourceMappingURL=numberMetricDependencies.js.map