// (C) 2026 GoodData Corporation
import { useCallback, useMemo } from "react";
import { useIntl } from "react-intl";
import { areObjRefsEqual, idRef, newAttribute, newMeasure, objRefToString, } from "@gooddata/sdk-model";
import { useBackendStrict, useWorkspaceStrict } from "@gooddata/sdk-ui";
import { getMeasureValueFilterConditionLabel } from "@gooddata/sdk-ui-filters";
import { useDashboardSelector } from "../../../model/react/DashboardStoreProvider.js";
import { selectCatalogAttributes, selectCatalogDateDatasets, selectCatalogIsLoaded, selectCatalogMeasures, } from "../../../model/store/catalog/catalogSelectors.js";
import { selectLocale, selectObjectAvailabilityConfig, selectSeparators, } from "../../../model/store/config/configSelectors.js";
const PERCENT_FORMAT_REGEX = /%/;
const SupportedCatalogGranularity = [
    "GDC.time.day_in_week",
    "GDC.time.day_in_month",
    "GDC.time.day_in_quarter",
    "GDC.time.day_in_year",
    "GDC.time.week_in_quarter",
    "GDC.time.week_in_year",
    "GDC.time.month_in_quarter",
    "GDC.time.month_in_year",
    "GDC.time.quarter_in_year",
    "GDC.time.fiscal_year",
    "GDC.time.fiscal_quarter",
    "GDC.time.fiscal_month",
];
function isPercentageFormat(format) {
    return !!format && PERCENT_FORMAT_REGEX.test(format);
}
/**
 * Normalizes the output of the SDK `<MeasureValueFilter />` onApply callback into a single
 * `conditions` array. The SDK can return either `condition` (single comparison/range) or
 * `conditions` (multi-condition OR); the dashboard model only uses `conditions`. Returns
 * undefined when the filter is "All" (no condition selected).
 *
 * @internal
 */
export function normalizeMeasureValueFilterConditions(updated) {
    const body = updated?.measureValueFilter;
    if (body?.conditions && body.conditions.length > 0) {
        return body.conditions;
    }
    if (body?.condition) {
        return [body.condition];
    }
    return undefined;
}
function findCatalogMetric(measure, measures) {
    return measures.find((m) => areObjRefsEqual(m.measure.ref, measure));
}
function catalogAttributeToDimensionalityItem(attribute) {
    return {
        identifier: attribute.defaultDisplayForm.ref,
        title: attribute.attribute.title,
        type: "attribute",
        ref: attribute.defaultDisplayForm.ref,
        dataset: attribute.dataSet
            ? {
                identifier: attribute.dataSet.ref,
                title: attribute.dataSet.title,
            }
            : undefined,
    };
}
function catalogDateDatasetToDimensionalityItems(dataset) {
    return dataset.dateAttributes.map((dateAttribute) => {
        return {
            identifier: dateAttribute.defaultDisplayForm.ref,
            title: dateAttribute.attribute.title,
            type: "chronologicalDate",
            ref: dateAttribute.defaultDisplayForm.ref,
            dataset: {
                identifier: dataset.dataSet.ref,
                title: dataset.dataSet.title,
            },
        };
    });
}
function getDimensionalityItem(ref, catalogDimensionality) {
    return (catalogDimensionality.find((item) => areObjRefsEqual(item.identifier, ref)) ?? {
        identifier: ref,
        title: objRefToString(ref),
        type: "attribute",
        ref,
    });
}
function buildMeasureValueFilterAvailabilityItems(measure, dimensionality) {
    const dimensionalityAttributes = dimensionality.map((ref) => newAttribute(ref));
    return [newMeasure(measure), ...dimensionalityAttributes];
}
async function loadValidDimensionalityAttributes(backend, workspace, items, includeObjectsWithTags, excludeObjectsWithTags) {
    const catalog = await backend
        .workspace(workspace)
        .catalog()
        .withOptions({
        types: ["attribute"],
        includeTags: (includeObjectsWithTags ?? []).map((tag) => idRef(tag)),
        excludeTags: (excludeObjectsWithTags ?? []).map((tag) => idRef(tag)),
        loadGroups: false,
    })
        .load();
    const availableCatalog = await catalog.availableItems().withOptions({ items }).load();
    return availableCatalog.availableAttributes();
}
async function loadAvailableDateDatasets(backend, workspace, items, includeObjectsWithTags, excludeObjectsWithTags) {
    const catalog = await backend
        .workspace(workspace)
        .catalog()
        .withOptions({
        types: ["dateDataset"],
        includeTags: (includeObjectsWithTags ?? []).map((tag) => idRef(tag)),
        excludeTags: (excludeObjectsWithTags ?? []).map((tag) => idRef(tag)),
        includeDateGranularities: SupportedCatalogGranularity,
        loadGroups: false,
    })
        .load();
    const availableCatalog = await catalog.availableItems().withOptions({ items }).load();
    return availableCatalog.availableDateDatasets();
}
async function loadCatalogDimensionalityItems(backend, workspace, measure, dimensionality, allCatalogDimensionality, includeObjectsWithTags, excludeObjectsWithTags) {
    const availabilityItems = buildMeasureValueFilterAvailabilityItems(measure, dimensionality);
    const [attributes, dateDatasets] = await Promise.all([
        loadValidDimensionalityAttributes(backend, workspace, availabilityItems, includeObjectsWithTags, excludeObjectsWithTags),
        loadAvailableDateDatasets(backend, workspace, availabilityItems, includeObjectsWithTags, excludeObjectsWithTags),
    ]);
    const availableItems = [
        ...attributes.map(catalogAttributeToDimensionalityItem),
        ...dateDatasets.flatMap(catalogDateDatasetToDimensionalityItems),
    ];
    return availableItems.filter((item) => allCatalogDimensionality.some((catalogItem) => areObjRefsEqual(catalogItem.identifier, item.identifier)));
}
/**
 * Hook that resolves the shared data needed to render a dashboard MVF dropdown.
 *
 * The host owns the choice of which filter to reflect in the UI: the persisted filter
 * (`filter`), or the working/staged filter when "Apply together" mode is active. Pass
 * the chosen one as `filterToDisplay`; if omitted, `filter` itself is used.
 *
 * @internal
 */
export function useDashboardMeasureValueFilterData(filter, filterToDisplay) {
    const backend = useBackendStrict();
    const workspace = useWorkspaceStrict();
    const measures = useDashboardSelector(selectCatalogMeasures);
    const attributes = useDashboardSelector(selectCatalogAttributes);
    const dateDatasets = useDashboardSelector(selectCatalogDateDatasets);
    const isCatalogLoaded = useDashboardSelector(selectCatalogIsLoaded);
    const objectAvailability = useDashboardSelector(selectObjectAvailabilityConfig);
    const separators = useDashboardSelector(selectSeparators);
    const locale = useDashboardSelector(selectLocale);
    const intl = useIntl();
    const { measure, localIdentifier, title: customTitle, dimensionality, } = filter.dashboardMeasureValueFilter;
    const effective = filterToDisplay ?? filter;
    const conditions = effective.dashboardMeasureValueFilter.conditions;
    const catalogMetric = useMemo(() => findCatalogMetric(measure, measures), [measure, measures]);
    const isMissingMetric = isCatalogLoaded && !catalogMetric;
    const defaultMetricTitle = catalogMetric?.measure.title ?? objRefToString(measure);
    const metricTitle = customTitle ?? defaultMetricTitle;
    const format = catalogMetric?.measure.format;
    const usePercentage = isPercentageFormat(format);
    const allCatalogDimensionality = useMemo(() => [
        ...attributes.map(catalogAttributeToDimensionalityItem),
        ...dateDatasets.flatMap(catalogDateDatasetToDimensionalityItems),
    ], [attributes, dateDatasets]);
    const dimensionalityItems = useMemo(() => (dimensionality ?? []).map((ref) => getDimensionalityItem(ref, allCatalogDimensionality)), [dimensionality, allCatalogDimensionality]);
    const loadCatalogDimensionality = useCallback((currentDimensionality) => loadCatalogDimensionalityItems(backend, workspace, measure, currentDimensionality, allCatalogDimensionality, objectAvailability?.includeObjectsWithTags, objectAvailability?.excludeObjectsWithTags), [
        allCatalogDimensionality,
        backend,
        measure,
        objectAvailability?.excludeObjectsWithTags,
        objectAvailability?.includeObjectsWithTags,
        workspace,
    ]);
    const conditionLabel = useMemo(() => getMeasureValueFilterConditionLabel(intl, conditions, { usePercentage, separators }), [intl, conditions, usePercentage, separators]);
    const dropdownFilter = useMemo(() => ({
        measureValueFilter: {
            measure,
            localIdentifier,
            ...(conditions && conditions.length > 0 ? { conditions } : {}),
            ...(dimensionality && dimensionality.length > 0 ? { dimensionality } : {}),
        },
    }), [measure, localIdentifier, conditions, dimensionality]);
    return {
        measure,
        localIdentifier,
        customTitle,
        catalogMetric,
        isMissingMetric,
        defaultMetricTitle,
        metricTitle,
        format,
        usePercentage,
        conditions,
        dimensionality,
        dimensionalityItems,
        catalogDimensionality: allCatalogDimensionality,
        loadCatalogDimensionality,
        conditionLabel,
        dropdownFilter,
        separators,
        locale,
        intl,
    };
}
/**
 * Build the shared `<MeasureValueFilter />` prop bundle from the data hook output.
 * Spread this into the SDK component and add host-specific props after.
 *
 * @internal
 */
export function getSharedDashboardMvfProps(data) {
    return {
        filter: data.dropdownFilter,
        measureIdentifier: data.localIdentifier,
        buttonTitle: data.metricTitle,
        measureTitle: data.metricTitle,
        usePercentage: data.usePercentage,
        format: data.format,
        useShortFormat: true,
        displayTreatNullAsZeroOption: true,
        separators: data.separators,
        locale: data.locale,
        enableOperatorSelection: true,
        enableMultipleConditions: true,
        isDimensionalityEnabled: false,
        isFilterSummaryEnabled: true,
        showSimplifiedSummary: true,
        isHeaderEnabled: true,
    };
}
