import { css } from '@emotion/css';
import { useCallback, useState } from 'react';

import { type GrafanaTheme2, type SelectableValue, type TimeRange } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { Trans, t } from '@grafana/i18n';
import { EditorField, EditorFieldGroup } from '@grafana/plugin-ui';
import { Button, InlineField, InlineFieldRow, Combobox, type ComboboxOption, useTheme2 } from '@grafana/ui';

import { METRIC_LABEL } from '../../constants';
import { type PrometheusDatasource } from '../../datasource';
import { type QueryBuilderLabelFilter } from '../shared/types';
import { type PromVisualQuery } from '../types';

import { formatKeyValueStrings } from './formatter';
import { MetricsModal } from './metrics-modal/MetricsModal';

export interface MetricComboboxProps {
  metricLookupDisabled: boolean;
  query: PromVisualQuery;
  onChange: (query: PromVisualQuery) => void;
  onGetMetrics: () => Promise<SelectableValue[]>;
  datasource: PrometheusDatasource;
  labelsFilters: QueryBuilderLabelFilter[];
  onBlur?: () => void;
  variableEditor?: boolean;
  timeRange: TimeRange;
}

export function MetricCombobox({
  datasource,
  query,
  onChange,
  onGetMetrics,
  labelsFilters,
  variableEditor,
  timeRange,
}: Readonly<MetricComboboxProps>) {
  const [metricsModalOpen, setMetricsModalOpen] = useState(false);
  const styles = getStyles(useTheme2());

  /**
   * Gets label_values response from prometheus API for current autocomplete query string and any existing labels filters
   */
  const getMetricLabels = useCallback(
    async (query: string) => {
      const match = formatKeyValueStrings(query, labelsFilters);
      const results = await datasource.languageProvider.queryLabelValues(timeRange, METRIC_LABEL, match);

      const resultsOptions = results.map((result) => {
        return {
          label: result,
          value: result,
        };
      });
      return resultsOptions;
    },
    [datasource.languageProvider, labelsFilters, timeRange]
  );

  const onComboboxChange = useCallback(
    (opt: ComboboxOption<string> | null) => {
      onChange({ ...query, metric: opt?.value ?? '' });
    },
    [onChange, query]
  );

  const loadOptions = useCallback(
    async (input: string): Promise<ComboboxOption[]> => {
      const metrics = input.length ? await getMetricLabels(input) : await onGetMetrics();

      return metrics.map((option) => ({
        label: option.label ?? option.value,
        value: option.value,
      }));
    },
    [getMetricLabels, onGetMetrics]
  );

  const asyncSelect = () => {
    return (
      <div className={styles.wrapper}>
        <Combobox
          placeholder={t(
            'grafana-prometheus.querybuilder.metric-combobox.async-select.placeholder-select-metric',
            'Select metric'
          )}
          width="auto"
          minWidth={25}
          options={loadOptions}
          value={query.metric}
          onChange={onComboboxChange}
          createCustomValue
          data-testid={selectors.components.DataSource.Prometheus.queryEditor.builder.metricSelect}
        />
        <Button
          tooltip={t(
            'grafana-prometheus.querybuilder.metric-combobox.async-select.tooltip-open-metrics-explorer',
            'Open metrics explorer'
          )}
          aria-label={t(
            'grafana-prometheus.querybuilder.metric-combobox.async-select.aria-label-open-metrics-explorer',
            'Open metrics explorer'
          )}
          variant="secondary"
          icon="book-open"
          className={styles.button}
          onClick={() => setMetricsModalOpen(true)}
        />
      </div>
    );
  };

  return (
    <>
      {!datasource.lookupsDisabled && metricsModalOpen && (
        <MetricsModal
          datasource={datasource}
          isOpen={metricsModalOpen}
          onClose={() => setMetricsModalOpen(false)}
          query={query}
          onChange={onChange}
          timeRange={timeRange}
        />
      )}
      {variableEditor ? (
        <InlineFieldRow>
          <InlineField
            label={t('grafana-prometheus.querybuilder.metric-combobox.label-metric', 'Metric')}
            labelWidth={20}
            tooltip={
              <div>
                <Trans i18nKey="grafana-prometheus.querybuilder.metric-combobox.tooltip-metric">
                  Optional: returns a list of label values for the label name in the specified metric.
                </Trans>
              </div>
            }
          >
            {asyncSelect()}
          </InlineField>
        </InlineFieldRow>
      ) : (
        <EditorFieldGroup>
          <EditorField label={t('grafana-prometheus.querybuilder.metric-combobox.label-metric', 'Metric')}>
            {asyncSelect()}
          </EditorField>
        </EditorFieldGroup>
      )}
    </>
  );
}

const getStyles = (theme: GrafanaTheme2) => {
  return {
    wrapper: css({
      display: 'flex',
      input: {
        borderTopRightRadius: 'unset',
        borderBottomRightRadius: 'unset',
      },
    }),
    button: css({
      borderTopLeftRadius: 'unset',
      borderBottomLeftRadius: 'unset',
    }),
  };
};
