import {
  EventBusSrv,
  type FieldConfigOptionsRegistry,
  type FieldConfigPropertyItem,
  FieldType,
  getDefaultTimeRange,
  LoadingState,
  type PanelPlugin,
  Registry,
  toDataFrame,
} from '@grafana/data';
import { VizPanel } from '@grafana/scenes';

import { getStandardEditorContext, getVisualizationOptions2 } from './getVisualizationOptions';

describe('getVisualizationOptions', () => {
  describe('getStandardEditorContext', () => {
    it('defaults the series data to an empty array', () => {
      const editorContext = getStandardEditorContext({
        data: undefined,
        replaceVariables: jest.fn(),
        options: {},
        eventBus: new EventBusSrv(),
        instanceState: {},
      });

      expect(editorContext.data).toEqual([]);
    });

    it('returns suggestions for empty data', () => {
      const editorContext = getStandardEditorContext({
        data: undefined,
        replaceVariables: jest.fn(),
        options: {},
        eventBus: new EventBusSrv(),
        instanceState: {},
      });

      expect(editorContext.getSuggestions).toBeDefined();
      expect(editorContext.getSuggestions?.()).toEqual([
        {
          documentation: 'Name of the series',
          label: 'Name',
          origin: 'series',
          value: '__series.name',
        },
        {
          documentation: 'Field name of the clicked datapoint (in ms epoch)',
          label: 'Name',
          origin: 'field',
          value: '__field.name',
        },
        {
          documentation: 'Display name of the field (includes overrides and transformations)',
          label: 'Display name',
          origin: 'field',
          value: '__field.displayName',
        },
        {
          documentation: 'Adds current variables',
          label: 'All variables',
          origin: 'template',
          value: '__all_variables',
        },
        {
          documentation: 'Adds current time range',
          label: 'Time range',
          origin: 'built-in',
          value: '__url_time_range',
        },
        {
          documentation: "Adds current time range's from value",
          label: 'Time range: from',
          origin: 'built-in',
          value: '__from',
        },
        {
          documentation: "Adds current time range's to value",
          label: 'Time range: to',
          origin: 'built-in',
          value: '__to',
        },
      ]);
    });

    it('returns suggestions for non-empty data', () => {
      const series = [
        toDataFrame({
          fields: [
            { name: 'time', type: FieldType.time },
            { name: 'score', type: FieldType.number },
          ],
        }),
      ];

      const panelData = {
        series,
        timeRange: getDefaultTimeRange(),
        state: LoadingState.Done,
      };

      const editorContext = getStandardEditorContext({
        data: panelData,
        replaceVariables: jest.fn(),
        options: {},
        eventBus: new EventBusSrv(),
        instanceState: {},
      });

      expect(editorContext.getSuggestions).toBeDefined();
      expect(editorContext.getSuggestions?.()).toEqual([
        {
          documentation: 'Name of the series',
          label: 'Name',
          origin: 'series',
          value: '__series.name',
        },
        {
          documentation: 'Field name of the clicked datapoint (in ms epoch)',
          label: 'Name',
          origin: 'field',
          value: '__field.name',
        },
        {
          documentation: 'Display name of the field (includes overrides and transformations)',
          label: 'Display name',
          origin: 'field',
          value: '__field.displayName',
        },
        {
          documentation: 'Formatted value for time on the same row',
          label: 'time',
          origin: 'fields',
          value: '__data.fields.time',
        },
        {
          documentation: 'Formatted value for score on the same row',
          label: 'score',
          origin: 'fields',
          value: '__data.fields.score',
        },
        {
          documentation: 'Enter the field order',
          label: 'Select by index',
          origin: 'fields',
          value: '__data.fields[0]',
        },
        {
          documentation: 'the numeric field value',
          label: 'Show numeric value',
          origin: 'fields',
          value: '__data.fields.score.numeric',
        },
        {
          documentation: 'the text value',
          label: 'Show text value',
          origin: 'fields',
          value: '__data.fields.score.text',
        },
        {
          documentation: 'Adds current variables',
          label: 'All variables',
          origin: 'template',
          value: '__all_variables',
        },
        {
          documentation: 'Adds current time range',
          label: 'Time range',
          origin: 'built-in',
          value: '__url_time_range',
        },
        {
          documentation: "Adds current time range's from value",
          label: 'Time range: from',
          origin: 'built-in',
          value: '__from',
        },
        {
          documentation: "Adds current time range's to value",
          label: 'Time range: to',
          origin: 'built-in',
          value: '__to',
        },
      ]);
    });
  });

  describe('getVisualizationOptions2', () => {
    it('should create an options list with the right number of categories and items', () => {
      const vizPanel = new VizPanel({
        title: 'Panel A',
        pluginId: 'timeseries',
        key: 'panel-12',
      });

      const property1: FieldConfigPropertyItem = {
        id: 'custom.property1', // Match field properties
        path: 'property1', // Match field properties
        isCustom: true,
        process: (value) => value,
        shouldApply: () => true,
        override: jest.fn(),
        editor: jest.fn(),
        name: 'Property 1',
      };

      const property2: FieldConfigPropertyItem = {
        id: 'custom.property2', // Match field properties
        path: 'property2', // Match field properties
        isCustom: true,
        process: (value) => value,
        shouldApply: () => true,
        override: jest.fn(),
        editor: jest.fn(),
        name: 'Property 2',
      };

      const property3: FieldConfigPropertyItem = {
        id: 'custom.property3.nested', // Match field properties
        path: 'property3.nested', // Match field properties
        isCustom: true,
        process: (value) => value,
        shouldApply: () => true,
        override: jest.fn(),
        editor: jest.fn(),
        name: 'Property 3',
      };

      const customFieldRegistry: FieldConfigOptionsRegistry = new Registry<FieldConfigPropertyItem>(() => {
        return [property1, property2, property3];
      });

      const plugin = {
        meta: { skipDataQuery: false },
        getPanelOptionsSupplier: jest.fn,
        fieldConfigRegistry: customFieldRegistry,
      } as unknown as PanelPlugin;

      const vizOptions = getVisualizationOptions2({
        panel: vizPanel,
        eventBus: new EventBusSrv(),
        plugin: plugin,
        instanceState: {},
      });

      expect(vizOptions.length).toEqual(1);
      expect(vizOptions[0].items.length).toEqual(3);
    });

    it('should not show items when the showIf evaluates to false', () => {
      const vizPanel = new VizPanel({
        title: 'Panel A',
        pluginId: 'timeseries',
        key: 'panel-12',
      });

      const property1: FieldConfigPropertyItem = {
        id: 'custom.property1', // Match field properties
        path: 'property1', // Match field properties
        isCustom: true,
        process: (value) => value,
        shouldApply: () => true,
        override: jest.fn(),
        editor: jest.fn(),
        name: 'Property 1',
        showIf: () => false,
      };

      const property2: FieldConfigPropertyItem = {
        id: 'custom.property2', // Match field properties
        path: 'property2', // Match field properties
        isCustom: true,
        process: (value) => value,
        shouldApply: () => true,
        override: jest.fn(),
        editor: jest.fn(),
        name: 'Property 2',
      };

      const property3: FieldConfigPropertyItem = {
        id: 'custom.property3.nested', // Match field properties
        path: 'property3.nested', // Match field properties
        isCustom: true,
        process: (value) => value,
        shouldApply: () => true,
        override: jest.fn(),
        editor: jest.fn(),
        name: 'Property 3',
      };

      const customFieldRegistry: FieldConfigOptionsRegistry = new Registry<FieldConfigPropertyItem>(() => {
        return [property1, property2, property3];
      });

      const plugin = {
        meta: { skipDataQuery: false },
        getPanelOptionsSupplier: jest.fn,
        fieldConfigRegistry: customFieldRegistry,
      } as unknown as PanelPlugin;

      const vizOptions = getVisualizationOptions2({
        panel: vizPanel,
        eventBus: new EventBusSrv(),
        plugin: plugin,
        instanceState: {},
      });

      expect(vizOptions.length).toEqual(1);
      expect(vizOptions[0].items.length).toEqual(2);
    });

    const fieldConfig = {
      defaults: {
        displayName: 'default',
        custom: {
          displayName: 'custom',
        },
      },
      overrides: [],
    };

    const vizPanel = new VizPanel({
      title: 'Panel A',
      pluginId: 'timeseries',
      key: 'panel-12',
      fieldConfig: fieldConfig,
    });

    const getOnePropVizPlugin = (isCustom: boolean, showIfSpy: jest.Mock) => {
      const property1: FieldConfigPropertyItem = {
        id: 'custom.property1', // Match field properties
        path: 'property1', // Match field properties
        isCustom: isCustom,
        process: (value) => value,
        shouldApply: () => true,
        override: jest.fn(),
        editor: jest.fn(),
        name: 'Property 1',
        showIf: showIfSpy,
      };

      const customFieldRegistry: FieldConfigOptionsRegistry = new Registry<FieldConfigPropertyItem>(() => {
        return [property1];
      });

      return {
        meta: { skipDataQuery: false },
        getPanelOptionsSupplier: jest.fn,
        fieldConfigRegistry: customFieldRegistry,
      } as unknown as PanelPlugin;
    };

    it('showIf should get custom fieldConfig if isCustom is true', () => {
      const showIfSpy = jest.fn().mockReturnValue(true);

      const plugin = getOnePropVizPlugin(true, showIfSpy);

      const vizOptions = getVisualizationOptions2({
        panel: vizPanel,
        eventBus: new EventBusSrv(),
        plugin: plugin,
        instanceState: {},
        data: {
          state: LoadingState.Done,
          series: [],
          timeRange: getDefaultTimeRange(),
          annotations: [
            {
              fields: [{ name: 'test', type: FieldType.string, config: { displayName: 'annotation' }, values: [1] }],
              length: 1,
            },
          ],
        },
      });

      expect(vizOptions.length).toEqual(1);
      expect(vizOptions[0].items.length).toEqual(1);
      expect(showIfSpy.mock.calls.length).toEqual(1);
      expect(showIfSpy.mock.calls[0][0].displayName).toBe('custom');
      expect(showIfSpy.mock.calls[0][2][0].fields[0].config.displayName).toBe('annotation');
    });

    it('showIf should get normal fieldConfig if isCustom is false', () => {
      const showIfSpy = jest.fn().mockReturnValue(true);

      const plugin = getOnePropVizPlugin(false, showIfSpy);

      const vizOptions = getVisualizationOptions2({
        panel: vizPanel,
        eventBus: new EventBusSrv(),
        plugin: plugin,
        instanceState: {},
        data: {
          state: LoadingState.Done,
          series: [],
          timeRange: getDefaultTimeRange(),
          annotations: [
            {
              fields: [{ name: 'test', type: FieldType.string, config: { displayName: 'annotation' }, values: [1] }],
              length: 1,
            },
          ],
        },
      });

      expect(vizOptions.length).toEqual(1);
      expect(vizOptions[0].items.length).toEqual(1);
      expect(showIfSpy.mock.calls.length).toEqual(1);
      expect(showIfSpy.mock.calls[0][0].displayName).toBe('default');
      expect(showIfSpy.mock.calls[0][2][0].fields[0].config.displayName).toBe('annotation');
    });
  });
});
