import { lastValueFrom, of, from, mergeMap, catchError, forkJoin, throwError, filter, take } from 'rxjs';
import { v4 } from 'uuid';
import { VariableSort, VariableRefresh, LoadingState, CoreApp } from '@grafana/data';
import { sceneGraph } from '../../../core/sceneGraph/index.js';
import { VariableDependencyConfig } from '../../VariableDependencyConfig.js';
import { MultiOrSingleValueSelect } from '../../components/VariableValueSelect.js';
import { MultiValueVariable } from '../MultiValueVariable.js';
import { createQueryVariableRunner } from './createQueryVariableRunner.js';
import { metricNamesToVariableValues, sortVariableValues } from './utils.js';
import { toMetricFindValues } from './toMetricFindValues.js';
import { getDataSource } from '../../../utils/getDataSource.js';
import { safeStringifyValue } from '../../utils.js';
import { SEARCH_FILTER_VARIABLE } from '../../constants.js';
import { debounce } from 'lodash';
import { registerQueryWithController } from '../../../querying/registerQueryWithController.js';
import { wrapInSafeSerializableSceneObject } from '../../../utils/wrapInSafeSerializableSceneObject.js';
import { expandMultiValueDatasourceUids } from '../../datasourceVariableRef.js';
import React from 'react';

class QueryVariable extends MultiValueVariable {
  constructor(initialState) {
    super({
      type: "query",
      name: "",
      value: "",
      text: "",
      options: [],
      datasource: null,
      regex: "",
      query: "",
      regexApplyTo: "value",
      refresh: VariableRefresh.onDashboardLoad,
      sort: VariableSort.disabled,
      ...initialState
    });
    this._variableDependency = new VariableDependencyConfig(this, {
      statePaths: ["regex", "regexApplyTo", "query", "datasource"]
    });
    this.onSearchChange = (searchFilter) => {
      if (!containsSearchFilter(this.state.query)) {
        return;
      }
      this._updateOptionsBasedOnSearchFilter(searchFilter);
    };
    this._updateOptionsBasedOnSearchFilter = debounce(async (searchFilter) => {
      const result = await lastValueFrom(this.getValueOptions({ searchFilter }));
      this.setState({ options: result, loading: false });
    }, 400);
  }
  getValueOptions(args) {
    if (!this.state.query) {
      return of([]);
    }
    this.setState({ loading: true, error: null });
    const fanOutUids = expandMultiValueDatasourceUids(this, this.state.datasource);
    const datasourceRefs = fanOutUids ? fanOutUids.map((uid) => ({ ...this.state.datasource, uid })) : [this.state.datasource];
    const scopedSceneObject = {
      __sceneObject: wrapInSafeSerializableSceneObject(this)
    };
    const perDatasource = datasourceRefs.map(
      (datasource) => from(getDataSource(datasource, scopedSceneObject)).pipe(
        mergeMap((ds) => this.runMetricFindForDatasource(ds, args)),
        catchError((error) => {
          if (error == null ? void 0 : error.cancelled) {
            return of({ values: [], error: void 0 });
          }
          return of({ values: [], error });
        })
      )
    );
    return forkJoin(perDatasource).pipe(
      mergeMap((results) => {
        const values = results.flatMap((result) => result.values);
        const errors = results.map((result) => result.error).filter(Boolean);
        if (values.length === 0 && errors.length > 0) {
          return throwError(() => errors[0]);
        }
        return of(this.metricFindValuesToOptions(values));
      })
    );
  }
  runMetricFindForDatasource(ds, args) {
    const runner = createQueryVariableRunner(ds);
    const target = runner.getTarget(this);
    const request = this.getRequest(target, args.searchFilter);
    return runner.runRequest({ variable: this, searchFilter: args.searchFilter }, request).pipe(
      registerQueryWithController({
        type: "QueryVariable/getValueOptions",
        request,
        origin: this
      }),
      filter((data) => data.state === LoadingState.Done || data.state === LoadingState.Error),
      take(1),
      mergeMap((data) => {
        if (data.state === LoadingState.Error) {
          return throwError(() => data.error);
        }
        return of(data);
      }),
      toMetricFindValues(),
      mergeMap((values) => of({ values, error: void 0 }))
    );
  }
  metricFindValuesToOptions(values) {
    let regex = "";
    if (this.state.regex) {
      regex = sceneGraph.interpolate(this, this.state.regex, void 0, "regex");
    }
    let options = metricNamesToVariableValues({
      variableRegEx: regex,
      variableRegexApplyTo: this.state.regexApplyTo,
      sort: this.state.sort,
      metricNames: values
    });
    if (this.state.staticOptions) {
      const customOptions = this.state.staticOptions;
      options = options.filter((option) => !customOptions.find((custom) => custom.value === option.value));
      if (this.state.staticOptionsOrder === "after") {
        options.push(...customOptions);
      } else if (this.state.staticOptionsOrder === "sorted") {
        options = sortVariableValues(options.concat(customOptions), this.state.sort);
      } else {
        options.unshift(...customOptions);
      }
    }
    return options;
  }
  getRequest(target, searchFilter) {
    const scopedVars = {
      __sceneObject: wrapInSafeSerializableSceneObject(this)
    };
    if (searchFilter) {
      scopedVars.__searchFilter = { value: searchFilter, text: searchFilter };
    }
    const range = sceneGraph.getTimeRange(this).state.value;
    const request = {
      app: CoreApp.Dashboard,
      requestId: v4(),
      timezone: "",
      range,
      interval: "",
      intervalMs: 0,
      // @ts-ignore
      targets: [target],
      scopedVars,
      startTime: Date.now()
    };
    return request;
  }
}
QueryVariable.Component = ({ model }) => {
  return /* @__PURE__ */ React.createElement(MultiOrSingleValueSelect, { model });
};
function containsSearchFilter(query) {
  const str = safeStringifyValue(query);
  return str.indexOf(SEARCH_FILTER_VARIABLE) > -1;
}

export { QueryVariable };
//# sourceMappingURL=QueryVariable.js.map
