// (C) 2021-2026 GoodData Corporation
import { createSelector } from "@reduxjs/toolkit";
import { areObjRefsEqual, attributeAlias, attributeDisplayFormRef, attributeLocalId, insightAttributes, insightMeasures, insightRef, measureAlias, measureItem, measureLocalId, measureTitle, } from "@gooddata/sdk-model";
import { newInsightMap } from "../../../_staging/metadata/objRefMap.js";
import { createMemoizedSelector } from "../_infra/selectors.js";
import { selectBackendCapabilities } from "../backendCapabilities/backendCapabilitiesSelectors.js";
import { selectCatalogAttributeDisplayForms, selectCatalogAttributes, selectCatalogDateDatasets, selectCatalogMeasures, } from "../catalog/catalogSelectors.js";
import { selectLocale } from "../config/configSelectors.js";
import { selectWidgetByRef } from "../tabs/layout/layoutSelectors.js";
import { insightsAdapter } from "./insightsEntityAdapter.js";
const entitySelectors = insightsAdapter.getSelectors((state) => state.insights);
/**
 * Selects all insights used on the dashboard.
 *
 * @remarks
 * Note: if you are aiming to lookup insights using an ObjRef, then you should instead use the map returned
 * by {@link selectInsightsMap}. If you are aiming to lookup a single insight by its ref, use {@link selectInsightByRef}.
 * Using these selectors is both faster and safer as they take ObjRef type into account and look up the insight
 * depending on the type of the ref.
 *
 * See {@link selectInsightsMap} or {@link selectInsightByRef} for a faster and safer ways to get
 * an insight by its ObjRef.
 * @public
 */
export const selectInsights = entitySelectors.selectAll;
/**
 * Selects refs of all insights used on the dashboard.
 *
 * @alpha
 */
export const selectInsightRefs = createSelector(selectInsights, (insights) => {
    return insights.map(insightRef);
});
/**
 * Selects all insights and returns them in a mapping of obj ref to the insight object.
 *
 * @alpha
 */
export const selectInsightsMap = createSelector(selectInsights, selectBackendCapabilities, (insights, capabilities) => {
    return newInsightMap(insights, capabilities.hasTypeScopedIdentifiers);
});
/**
 * Selects insight used on a dashboard by its ref.
 *
 * @alpha
 */
export const selectInsightByRef = createMemoizedSelector((ref) => {
    return createSelector(selectInsightsMap, (insights) => {
        return ref && insights.get(ref);
    });
});
/**
 * Selects insight used on a dashboard by widget ref.
 *
 * @alpha
 */
export const selectInsightByWidgetRef = createMemoizedSelector((ref) => {
    return createSelector(selectWidgetByRef(ref), selectInsightsMap, (widget, insights) => {
        const ref = widget?.insight;
        return ref && insights.get(ref);
    });
});
/**
 * Selects raw export custom measure overrides for insight by ref.
 *
 * @privateRemarks
 * For a given insight (identified by its ref), this selector will return a mapping of measure localId to
 * the custom title that should be used when raw exporting the insight.
 * The order of precedence is:
 * 1. If the measure has an alias in the insight definition, use that.
 * 2. If the measure has a title in the insight definition, use that.
 * 3. Use the title of the measure from the catalog.
 *
 * @alpha
 * @deprecated This selector does not fill in missing titles for derived and arithmetic measures.
 */
const selectRawExportMeasureOverridesForInsightByRef = createMemoizedSelector((ref) => {
    return createSelector(selectInsightByRef(ref), selectCatalogMeasures, selectLocale, (insight, catalogMeasures, _locale) => {
        if (!insight) {
            return undefined;
        }
        return insightMeasures(insight).reduce((overrides, measure) => {
            const localId = measureLocalId(measure);
            // first, try getting the title from the insight itself,
            // giving precedence to the alias over the title.
            // this should also cover the case of derived measures without renames,
            // because they have been processed by fillMissingTitles
            const titleFromInsightMeasure = measureAlias(measure) || measureTitle(measure);
            if (titleFromInsightMeasure) {
                overrides[localId] = {
                    title: titleFromInsightMeasure,
                };
                return overrides;
            }
            // otherwise, get it from the catalog.
            // we only need to look at the measures, any fact-based measures should have the title set in the
            // insight itself
            const catalogMeasure = catalogMeasures.find((m) => areObjRefsEqual(m.measure.ref, measureItem(measure)));
            if (catalogMeasure) {
                overrides[localId] = {
                    title: catalogMeasure.measure.title,
                };
            }
            return overrides;
        }, {});
    });
});
/**
 * Selects raw export custom measure overrides for insight.
 *
 * @privateRemarks
 * For a given insight, this selector will return a mapping of measure localId to
 * the custom title that should be used when raw exporting the insight.
 * The order of precedence is:
 * 1. If the measure has an alias in the insight definition, use that.
 * 2. If the measure has a title in the insight definition, use that.
 * 3. Use the title of the measure from the catalog.
 *
 * @alpha
 */
const selectRawExportMeasureOverridesForInsight = createMemoizedSelector((insight) => {
    return createSelector(selectCatalogMeasures, (catalogMeasures) => {
        if (!insight) {
            return undefined;
        }
        // the insight needs to have missing titles for derived and arithmetic measures already filled
        return insightMeasures(insight).reduce((overrides, measure) => {
            const localId = measureLocalId(measure);
            // first, try getting the title from the insight itself,
            // giving precedence to the alias over the title.
            // this should also cover the case of derived measures without renames,
            // because they have been processed by fillMissingTitles
            const titleFromInsightMeasure = measureAlias(measure) || measureTitle(measure);
            if (titleFromInsightMeasure) {
                overrides[localId] = {
                    title: titleFromInsightMeasure,
                };
                return overrides;
            }
            // otherwise, get it from the catalog.
            // we only need to look at the measures, any fact-based measures should have the title set in the
            // insight itself
            const catalogMeasure = catalogMeasures.find((m) => areObjRefsEqual(m.measure.ref, measureItem(measure)));
            if (catalogMeasure) {
                overrides[localId] = {
                    title: catalogMeasure.measure.title,
                };
            }
            return overrides;
        }, {});
    });
});
/**
 * For geo display forms (latitude, longitude, area, pin), resolves the parent attribute's
 * default display form title instead of the geo-specific display form title.
 * Returns the original title for non-geo display forms.
 */
function resolveDisplayFormTitle(catalogDisplayFormTitle, catalogDisplayFormType, catalogDisplayFormAttributeRef, catalogAttributes) {
    if (typeof catalogDisplayFormType === "string" && catalogDisplayFormType.startsWith("GDC.geo.")) {
        const parentCatalogAttribute = catalogAttributes.find((ca) => areObjRefsEqual(ca.attribute.ref, catalogDisplayFormAttributeRef));
        if (parentCatalogAttribute) {
            return parentCatalogAttribute.defaultDisplayForm.title;
        }
    }
    return catalogDisplayFormTitle;
}
/**
 * Selects raw export custom display form overrides for insight by ref.
 *
 * @privateRemarks
 * For a given insight (identified by its ref), this selector will return a mapping of attribute localId to
 * the custom title that should be used when raw exporting the insight.
 * The order of precedence is:
 * 1. If the attribute has an alias in the insight definition, use that.
 * 2. Use the title of the attribute from the catalog, trying normal attributes first and then date datasets.
 *    For geo display forms, the parent attribute's default display form title is used instead.
 *
 * @alpha
 */
const selectRawExportDisplayFormOverridesForInsight = createMemoizedSelector((insight) => {
    return createSelector(selectCatalogAttributeDisplayForms, selectCatalogDateDatasets, selectCatalogAttributes, (catalogAttributeDisplayForms, catalogDateDatasets, catalogAttributes) => {
        if (!insight) {
            return undefined;
        }
        return insightAttributes(insight).reduce((overrides, attribute) => {
            const localId = attributeLocalId(attribute);
            // first, try getting the title from the insight itself
            const titleFromInsightAttribute = attributeAlias(attribute);
            if (titleFromInsightAttribute) {
                overrides[localId] = {
                    title: titleFromInsightAttribute,
                };
                return overrides;
            }
            // otherwise, get it from the catalog.
            // first try the attributes
            const catalogDisplayForm = catalogAttributeDisplayForms.find((a) => areObjRefsEqual(a.ref, attributeDisplayFormRef(attribute)));
            if (catalogDisplayForm) {
                overrides[localId] = {
                    title: resolveDisplayFormTitle(catalogDisplayForm.title, catalogDisplayForm.displayFormType, catalogDisplayForm.attribute, catalogAttributes),
                };
                return overrides;
            }
            // then try the date datasets
            catalogDateDatasets.forEach((dateDataset) => {
                const catalogDateAttribute = dateDataset.dateAttributes.find((a) => areObjRefsEqual(a.defaultDisplayForm.ref, attributeDisplayFormRef(attribute)));
                if (catalogDateAttribute) {
                    overrides[localId] = {
                        title: catalogDateAttribute.defaultDisplayForm.title,
                    };
                }
            });
            return overrides;
        }, {});
    });
});
/**
 * Selects raw export custom display form overrides for insight by ref.
 *
 * @privateRemarks
 * For a given insight (identified by its ref), this selector will return a mapping of attribute localId to
 * the custom title that should be used when raw exporting the insight.
 * The order of precedence is:
 * 1. If the attribute has an alias in the insight definition, use that.
 * 2. Use the title of the attribute from the catalog, trying normal attributes first and then date datasets.
 *    For geo display forms, the parent attribute's default display form title is used instead.
 *
 * @alpha
 */
const selectRawExportDisplayFormOverridesForInsightByRef = createMemoizedSelector((ref) => {
    return createSelector(selectInsightByRef(ref), selectCatalogAttributeDisplayForms, selectCatalogDateDatasets, selectCatalogAttributes, (insight, catalogAttributeDisplayForms, catalogDateDatasets, catalogAttributes) => {
        if (!insight) {
            return undefined;
        }
        return insightAttributes(insight).reduce((overrides, attribute) => {
            const localId = attributeLocalId(attribute);
            // first, try getting the title from the insight itself
            const titleFromInsightAttribute = attributeAlias(attribute);
            if (titleFromInsightAttribute) {
                overrides[localId] = {
                    title: titleFromInsightAttribute,
                };
                return overrides;
            }
            // otherwise, get it from the catalog.
            // first try the attributes
            const catalogDisplayForm = catalogAttributeDisplayForms.find((a) => areObjRefsEqual(a.ref, attributeDisplayFormRef(attribute)));
            if (catalogDisplayForm) {
                overrides[localId] = {
                    title: resolveDisplayFormTitle(catalogDisplayForm.title, catalogDisplayForm.displayFormType, catalogDisplayForm.attribute, catalogAttributes),
                };
                return overrides;
            }
            // then try the date datasets
            catalogDateDatasets.forEach((dateDataset) => {
                const catalogDateAttribute = dateDataset.dateAttributes.find((a) => areObjRefsEqual(a.defaultDisplayForm.ref, attributeDisplayFormRef(attribute)));
                if (catalogDateAttribute) {
                    overrides[localId] = {
                        title: catalogDateAttribute.defaultDisplayForm.title,
                    };
                }
            });
            return overrides;
        }, {});
    });
});
/**
 * Selects raw export custom overrides for insight by ref.
 *
 * @deprecated The insights reached by refs don't have missing titles for derived and arithmetic measures filled in.
 * @alpha
 */
export const selectRawExportOverridesForInsightByRef = createMemoizedSelector((ref) => {
    return createSelector(selectRawExportMeasureOverridesForInsightByRef(ref), selectRawExportDisplayFormOverridesForInsightByRef(ref), (measureOverrides, displayFormOverrides) => {
        return {
            measures: measureOverrides,
            displayForms: displayFormOverrides,
        };
    });
});
/**
 * Selects raw export custom overrides for insight. Note that it's recommended to use
 * fillMissingTitles to fill in missing titles for derived and arithmetic measures.
 *
 * @alpha
 */
export const selectRawExportOverridesForInsight = createMemoizedSelector((insight) => {
    return createSelector(selectRawExportMeasureOverridesForInsight(insight), selectRawExportDisplayFormOverridesForInsight(insight), (measureOverrides, displayFormOverrides) => {
        return {
            measures: measureOverrides,
            displayForms: displayFormOverrides,
        };
    });
});
