// (C) 2026 GoodData Corporation
import { useMemo } from "react";
import { areObjRefsEqual, insightBuckets, insightFilters, insightMeasures, isObjRef, measureItem, objRefToString, } from "@gooddata/sdk-model";
import { useBackendStrict, useCancelablePromise, useWorkspaceStrict } from "@gooddata/sdk-ui";
const COMPATIBLE_MEASURE_REFS_CACHE_SIZE = 50;
const compatibleMeasureRefsCache = new Map();
const compatibleMeasureRefsPromises = new Map();
export function useMeasureValueFilterCompatibility(insight, filters) {
    const backend = useBackendStrict();
    const workspace = useWorkspaceStrict();
    const measureRefs = useMemo(() => filters.map((filter) => filter.dashboardMeasureValueFilter.measure), [filters]);
    const measureRefsDigest = useMemo(() => measureRefs.map(objRefToString).join("|"), [measureRefs]);
    const insightIdentifier = getInsightIdentifier(insight);
    const cacheKey = useMemo(() => insight && measureRefs.length
        ? getCompatibleMeasureRefsCacheKey(workspace, insight, measureRefsDigest)
        : undefined, [insight, measureRefs.length, measureRefsDigest, workspace]);
    const cachedCompatibleMeasureRefs = useMemo(() => {
        if (!measureRefs.length) {
            return [];
        }
        if (!insight) {
            return measureRefs;
        }
        return cacheKey ? getCachedValue(compatibleMeasureRefsCache, cacheKey) : undefined;
    }, [cacheKey, insight, measureRefs]);
    const insightToLoadCompatibleMeasureRefsFor = cachedCompatibleMeasureRefs ? undefined : insight;
    const { result: loadedCompatibleMeasureRefs, status } = useCancelablePromise({
        promise: insightToLoadCompatibleMeasureRefsFor
            ? async () => loadCompatibleMeasureRefs(backend, workspace, insightToLoadCompatibleMeasureRefsFor, measureRefs)
            : undefined,
    }, [
        backend,
        workspace,
        measureRefsDigest,
        insightIdentifier,
        cachedCompatibleMeasureRefs,
        insightToLoadCompatibleMeasureRefsFor,
    ]);
    const compatibleMeasureRefs = cachedCompatibleMeasureRefs ?? loadedCompatibleMeasureRefs;
    const compatibleMeasureValueFilters = useMemo(() => compatibleMeasureRefs
        ? filters.filter((filter) => isMeasureRefCompatible(filter.dashboardMeasureValueFilter.measure, compatibleMeasureRefs))
        : filters, [compatibleMeasureRefs, filters]);
    return {
        status: cachedCompatibleMeasureRefs ? "success" : status,
        isCompatible: (measureRef) => isMeasureRefCompatible(measureRef, compatibleMeasureRefs),
        compatibleMeasureRefs,
        compatibleMeasureValueFilters,
    };
}
function isMeasureRefCompatible(measureRef, compatibleMeasureRefs) {
    return (!compatibleMeasureRefs ||
        compatibleMeasureRefs.some((compatibleMeasureRef) => areObjRefsEqual(compatibleMeasureRef, measureRef)));
}
export async function loadCompatibleMeasureRefs(backend, workspace, insight, measureRefs) {
    const cacheKey = getCompatibleMeasureRefsCacheKey(workspace, insight, measureRefs.map(objRefToString).join("|"));
    const cachedCompatibleMeasureRefs = getCachedValue(compatibleMeasureRefsCache, cacheKey);
    if (cachedCompatibleMeasureRefs) {
        return cachedCompatibleMeasureRefs;
    }
    const pendingCompatibleMeasureRefs = getCachedValue(compatibleMeasureRefsPromises, cacheKey);
    if (pendingCompatibleMeasureRefs) {
        return pendingCompatibleMeasureRefs;
    }
    const compatibleMeasureRefsPromise = loadCompatibleMeasureRefsFromBackend(backend, workspace, insight, measureRefs)
        .then((compatibleMeasureRefs) => {
        setCachedValue(compatibleMeasureRefsCache, cacheKey, compatibleMeasureRefs);
        return compatibleMeasureRefs;
    })
        .finally(() => {
        compatibleMeasureRefsPromises.delete(cacheKey);
    });
    setCachedValue(compatibleMeasureRefsPromises, cacheKey, compatibleMeasureRefsPromise);
    return compatibleMeasureRefsPromise;
}
async function loadCompatibleMeasureRefsFromBackend(backend, workspace, insight, measureRefs) {
    const catalog = await backend
        .workspace(workspace)
        .catalog()
        .withGroups(false)
        .forTypes(["measure"])
        .load();
    const availableCatalog = await catalog.availableItems().forTypes(["measure"]).forInsight(insight).load();
    const insightMeasureRefs = insightMeasures(insight)
        .map(measureItem)
        .filter((ref) => !!ref);
    const availableMeasureRefs = availableCatalog.availableMeasures().map((measure) => measure.measure.ref);
    const validMeasureRefs = [...availableMeasureRefs, ...insightMeasureRefs];
    return measureRefs.filter((measureRef) => validMeasureRefs.some((validMeasureRef) => areObjRefsEqual(validMeasureRef, measureRef)));
}
function getCompatibleMeasureRefsCacheKey(workspace, insight, measureRefsDigest) {
    return JSON.stringify([
        workspace,
        getInsightIdentifier(insight),
        insightBuckets(insight),
        insightFilters(insight),
        measureRefsDigest,
    ]);
}
function getInsightIdentifier(insight) {
    if (!insight) {
        return undefined;
    }
    if ("identifier" in insight.insight && typeof insight.insight.identifier === "string") {
        return insight.insight.identifier;
    }
    if ("ref" in insight.insight && isObjRef(insight.insight.ref)) {
        return objRefToString(insight.insight.ref);
    }
    return undefined;
}
function getCachedValue(cache, key) {
    return cache.get(key);
}
function setCachedValue(cache, key, value) {
    cache.delete(key);
    cache.set(key, value);
    while (cache.size > COMPATIBLE_MEASURE_REFS_CACHE_SIZE) {
        const oldestKey = cache.keys().next().value;
        if (oldestKey === undefined) {
            return;
        }
        cache.delete(oldestKey);
    }
}
