import React from 'react';
import { store } from '@grafana/data';
import { sceneGraph } from '../../core/sceneGraph/index.js';
import { getEnrichedDataRequest } from '../../querying/getEnrichedDataRequest.js';
import { getQueriesForVariables } from '../utils.js';
import { getDataSource } from '../../utils/getDataSource.js';
import { DrilldownRecommendations } from '../components/DrilldownRecommendations.js';
import { ScopesVariable } from '../variants/ScopesVariable.js';
import { SCOPES_VARIABLE_NAME } from '../constants.js';
import { getAdHocFilterInteractionHandler } from '../../core/sceneGraph/getReportInteractionHandler.js';
import { AdHocFiltersVariable, isGroupByFilter, isFilterComplete } from './AdHocFiltersVariable.js';
import { SceneObjectBase } from '../../core/SceneObjectBase.js';
import { wrapInSafeSerializableSceneObject } from '../../utils/wrapInSafeSerializableSceneObject.js';

const getRecentFiltersKey = (datasourceUid) => `grafana.filters.recent.${datasourceUid != null ? datasourceUid : "default"}`;
const getRecentGroupingKey$1 = (datasourceUid) => `grafana.grouping.recent.${datasourceUid != null ? datasourceUid : "default"}`;
function getFilterIdentity(filter) {
  return `${filter.key}|${filter.operator}|${filter.value}`;
}
function deduplicateFilters(filters) {
  const filterMap = /* @__PURE__ */ new Map();
  for (const filter of filters) {
    const identity = getFilterIdentity(filter);
    if (filterMap.has(identity)) {
      filterMap.delete(identity);
    }
    filterMap.set(identity, filter);
  }
  return Array.from(filterMap.values());
}
function deduplicateGroupings(groupings) {
  const seen = /* @__PURE__ */ new Map();
  for (const g of groupings) {
    const key = String(g.value);
    if (seen.has(key)) {
      seen.delete(key);
    }
    seen.set(key, g);
  }
  return Array.from(seen.values());
}
class AdHocFiltersRecommendations extends SceneObjectBase {
  constructor(state = {}) {
    super(state);
    this._activationHandler = () => {
      const filterJson = store.get(this._getFiltersStorageKey());
      const storedFilters = filterJson ? JSON.parse(filterJson) : [];
      if (storedFilters.length > 0) {
        this._verifyRecentFiltersApplicability(storedFilters);
      } else {
        this.setState({ recentFilters: [] });
      }
      if (this._isGroupByEnabled) {
        const groupingJson = store.get(this._getGroupingStorageKey());
        const storedGroupings = groupingJson ? JSON.parse(groupingJson) : [];
        if (storedGroupings.length > 0) {
          this._verifyRecentGroupingsApplicability(storedGroupings);
        } else {
          this.setState({ recentGrouping: [] });
        }
      }
      this._fetchRecommendedDrilldowns();
      const scopesVariable = sceneGraph.lookupVariable(SCOPES_VARIABLE_NAME, this._adHocFilter);
      let scopesSubscription;
      let adHocSubscription;
      if (scopesVariable instanceof ScopesVariable) {
        this._subs.add(
          scopesSubscription = scopesVariable.subscribeToState((newState, prevState) => {
            if (newState.scopes !== prevState.scopes) {
              this._reloadStoredFilters();
              if (this._isGroupByEnabled) {
                this._reloadStoredGroupings();
              }
              this._fetchRecommendedDrilldowns();
            }
          })
        );
      }
      this._subs.add(
        adHocSubscription = this._adHocFilter.subscribeToState((newState, prevState) => {
          if (newState.filters !== prevState.filters) {
            this._reloadStoredFilters();
            if (this._isGroupByEnabled) {
              this._reloadStoredGroupings();
            }
            this._fetchRecommendedDrilldowns();
          }
        })
      );
      return () => {
        scopesSubscription == null ? void 0 : scopesSubscription.unsubscribe();
        adHocSubscription == null ? void 0 : adHocSubscription.unsubscribe();
      };
    };
    this.addActivationHandler(this._activationHandler);
  }
  get _adHocFilter() {
    if (!(this.parent instanceof AdHocFiltersVariable)) {
      throw new Error("AdHocFiltersRecommendations must be a child of AdHocFiltersVariable");
    }
    return this.parent;
  }
  get _scopedVars() {
    return { __sceneObject: wrapInSafeSerializableSceneObject(this._adHocFilter) };
  }
  get _isGroupByEnabled() {
    return this._adHocFilter.state.enableGroupBy === true;
  }
  _reloadStoredFilters() {
    const json = store.get(this._getFiltersStorageKey());
    const storedFilters = json ? JSON.parse(json) : [];
    if (storedFilters.length > 0) {
      this._verifyRecentFiltersApplicability(storedFilters);
    }
  }
  _reloadStoredGroupings() {
    const json = store.get(this._getGroupingStorageKey());
    const storedGroupings = json ? JSON.parse(json) : [];
    if (storedGroupings.length > 0) {
      this._verifyRecentGroupingsApplicability(storedGroupings);
    }
  }
  _getFiltersStorageKey() {
    var _a;
    return getRecentFiltersKey((_a = this._adHocFilter.state.datasource) == null ? void 0 : _a.uid);
  }
  _getGroupingStorageKey() {
    var _a;
    return getRecentGroupingKey$1((_a = this._adHocFilter.state.datasource) == null ? void 0 : _a.uid);
  }
  async _fetchRecommendedDrilldowns() {
    var _a;
    const adhoc = this._adHocFilter;
    const ds = await getDataSource(adhoc.state.datasource, this._scopedVars);
    if (!ds || !ds.getRecommendedDrilldowns) {
      this.setState({ datasourceSupportsRecommendations: false });
      return;
    }
    this.setState({ datasourceSupportsRecommendations: true });
    const queries = adhoc.state.useQueriesAsFilterForOptions ? getQueriesForVariables(adhoc) : void 0;
    const timeRange = sceneGraph.getTimeRange(adhoc).state.value;
    const scopes = sceneGraph.getScopes(adhoc);
    const allFilters = [...(_a = adhoc.state.originFilters) != null ? _a : [], ...adhoc.state.filters];
    const filters = allFilters.filter((f) => !isGroupByFilter(f));
    const groupByKeys = this._isGroupByEnabled ? allFilters.filter((f) => isGroupByFilter(f)).map((f) => f.key) : void 0;
    const enrichedRequest = getEnrichedDataRequest(adhoc);
    const dashboardUid = enrichedRequest == null ? void 0 : enrichedRequest.dashboardUID;
    try {
      const recommendedDrilldowns = await ds.getRecommendedDrilldowns({
        timeRange,
        dashboardUid,
        queries: queries != null ? queries : [],
        filters,
        ...groupByKeys ? { groupByKeys } : {},
        scopes
      });
      const stateUpdate = {};
      if (recommendedDrilldowns == null ? void 0 : recommendedDrilldowns.filters) {
        stateUpdate.recommendedFilters = recommendedDrilldowns.filters;
      }
      if (this._isGroupByEnabled && (recommendedDrilldowns == null ? void 0 : recommendedDrilldowns.groupByKeys)) {
        stateUpdate.recommendedGrouping = recommendedDrilldowns.groupByKeys.map((key) => ({
          value: key,
          text: key
        }));
      }
      this.setState(stateUpdate);
    } catch (error) {
      console.error("Failed to fetch recommended drilldowns:", error);
    }
  }
  async _verifyRecentFiltersApplicability(storedFilters) {
    const adhoc = this._adHocFilter;
    const queries = adhoc.state.useQueriesAsFilterForOptions ? getQueriesForVariables(adhoc) : void 0;
    const response = await adhoc.getFiltersApplicabilityForQueries(storedFilters, queries != null ? queries : []);
    if (!response) {
      const deduped = deduplicateFilters(storedFilters);
      this.setState({ recentFilters: deduped.slice(-3) });
      return;
    }
    const applicabilityMap = /* @__PURE__ */ new Map();
    response.forEach((item) => {
      applicabilityMap.set(item.key, item.applicable !== false);
    });
    const applicableFilters = storedFilters.filter((f) => {
      const isApplicable = applicabilityMap.get(f.key);
      return isApplicable === void 0 || isApplicable === true;
    });
    const recentFilters = deduplicateFilters(applicableFilters).slice(-3);
    this.setState({ recentFilters });
  }
  async _verifyRecentGroupingsApplicability(storedGroupings) {
    const adhoc = this._adHocFilter;
    const queries = adhoc.state.useQueriesAsFilterForOptions ? getQueriesForVariables(adhoc) : void 0;
    const groupByKeys = storedGroupings.map((g) => String(g.value));
    const response = await adhoc.getFiltersApplicabilityForQueries([], queries != null ? queries : [], groupByKeys);
    if (!response) {
      this.setState({ recentGrouping: deduplicateGroupings(storedGroupings).slice(-3) });
      return;
    }
    const applicabilityMap = /* @__PURE__ */ new Map();
    response.forEach((item) => {
      applicabilityMap.set(item.key, item.applicable !== false);
    });
    const applicableGroupings = deduplicateGroupings(storedGroupings).filter((g) => {
      const isApplicable = applicabilityMap.get(String(g.value));
      return isApplicable === void 0 || isApplicable === true;
    }).slice(-3);
    this.setState({ recentGrouping: applicableGroupings });
  }
  /**
   * Stores a recent filter in localStorage and updates state.
   * No-op for filters that are not yet complete (key/operator/value all set)
   */
  storeRecentFilter(filter) {
    if (!isFilterComplete(filter)) {
      return;
    }
    const key = this._getFiltersStorageKey();
    const storedFilters = store.get(key);
    const allRecentFilters = storedFilters ? JSON.parse(storedFilters) : [];
    const updatedStoredFilters = deduplicateFilters([...allRecentFilters, filter]).slice(-10);
    store.set(key, JSON.stringify(updatedStoredFilters));
    const adhoc = this._adHocFilter;
    const existingFilter = adhoc.state.filters.find((f) => f.key === filter.key && !Boolean(f.nonApplicable));
    if (existingFilter && !Boolean(existingFilter.nonApplicable)) {
      this.setState({ recentFilters: updatedStoredFilters.slice(-3) });
    }
  }
  /**
   * Stores a recent grouping key in localStorage and updates state.
   * No-op when enableGroupBy is false or when the key is empty.
   */
  storeRecentGrouping(groupByKey) {
    if (!this._isGroupByEnabled || !groupByKey) {
      return;
    }
    const storageKey = this._getGroupingStorageKey();
    const storedGroupings = store.get(storageKey);
    const allRecentGroupings = storedGroupings ? JSON.parse(storedGroupings) : [];
    const withoutDuplicate = allRecentGroupings.filter((g) => String(g.value) !== groupByKey);
    const updated = [...withoutDuplicate, { value: groupByKey, text: groupByKey }];
    const limited = updated.slice(-10);
    store.set(storageKey, JSON.stringify(limited));
    this.setState({ recentGrouping: limited.slice(-3) });
  }
  addFilterToParent(filter) {
    this._adHocFilter.updateFilters([...this._adHocFilter.state.filters, filter]);
  }
  addGroupByToParent(key) {
    if (!this._isGroupByEnabled) {
      return;
    }
    const adhoc = this._adHocFilter;
    const exists = adhoc.state.filters.some((f) => isGroupByFilter(f) && f.key === key);
    if (exists) {
      return;
    }
    adhoc._addGroupByFilter({ value: key, label: key });
  }
}
AdHocFiltersRecommendations.Component = AdHocFiltersRecommendationsRenderer;
function AdHocFiltersRecommendationsRenderer({ model }) {
  const { recentFilters, recommendedFilters, datasourceSupportsRecommendations } = model.useState();
  const { filters } = model._adHocFilter.useState();
  const recentDrilldowns = recentFilters == null ? void 0 : recentFilters.map((filter) => ({
    label: `${filter.key} ${filter.operator} ${filter.value}`,
    onClick: () => {
      var _a, _b;
      const exists = filters.some((f) => f.key === filter.key && f.value === filter.value);
      if (!exists) {
        model.addFilterToParent(filter);
        (_b = (_a = getAdHocFilterInteractionHandler(model)) == null ? void 0 : _a.onRecentFilterApplied) == null ? void 0 : _b.call(_a, {
          key: filter.key,
          operator: filter.operator
        });
      }
    }
  }));
  const recommendedDrilldowns = recommendedFilters == null ? void 0 : recommendedFilters.map((filter) => ({
    label: `${filter.key} ${filter.operator} ${filter.value}`,
    onClick: () => {
      var _a, _b;
      const exists = filters.some((f) => f.key === filter.key && f.value === filter.value);
      if (!exists) {
        model.addFilterToParent(filter);
        (_b = (_a = getAdHocFilterInteractionHandler(model)) == null ? void 0 : _a.onRecommendedFilterApplied) == null ? void 0 : _b.call(_a, {
          key: filter.key,
          operator: filter.operator
        });
      }
    }
  }));
  return /* @__PURE__ */ React.createElement(
    DrilldownRecommendations,
    {
      recentDrilldowns,
      recommendedDrilldowns,
      showRecommended: datasourceSupportsRecommendations
    }
  );
}
function AdHocGroupByRecommendationsRenderer({ model }) {
  const { recentGrouping, recommendedGrouping, datasourceSupportsRecommendations } = model.useState();
  const recentDrilldowns = recentGrouping == null ? void 0 : recentGrouping.map((groupBy) => ({
    label: `${groupBy.value}`,
    onClick: () => {
      var _a, _b;
      model.addGroupByToParent(String(groupBy.value));
      (_b = (_a = getAdHocFilterInteractionHandler(model)) == null ? void 0 : _a.onRecentGroupByApplied) == null ? void 0 : _b.call(_a, { key: String(groupBy.value) });
    }
  }));
  const recommendedDrilldowns = recommendedGrouping == null ? void 0 : recommendedGrouping.map((groupBy) => ({
    label: `${groupBy.value}`,
    onClick: () => {
      var _a, _b;
      model.addGroupByToParent(String(groupBy.value));
      (_b = (_a = getAdHocFilterInteractionHandler(model)) == null ? void 0 : _a.onRecommendedGroupByApplied) == null ? void 0 : _b.call(_a, { key: String(groupBy.value) });
    }
  }));
  return /* @__PURE__ */ React.createElement(
    DrilldownRecommendations,
    {
      recentDrilldowns,
      recommendedDrilldowns,
      showRecommended: datasourceSupportsRecommendations
    }
  );
}

export { AdHocFiltersRecommendations, AdHocGroupByRecommendationsRenderer, getRecentFiltersKey, getRecentGroupingKey$1 as getRecentGroupingKey };
//# sourceMappingURL=AdHocFiltersRecommendations.js.map
