import { act, fireEvent, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { render } from 'test/test-utils';

import { standardEditorsRegistry, standardFieldConfigEditorRegistry } from '@grafana/data';
import { getPanelPlugin } from '@grafana/data/test/__mocks__/pluginMocks';
import { selectors } from '@grafana/e2e-selectors';
import { VizPanel } from '@grafana/scenes';
import { getAllOptionEditors, getAllStandardFieldConfigs } from 'app/core/components/OptionsUI/registry';
import { OptionFilter } from 'app/features/dashboard/components/PanelEditor/OptionsPaneOptions';
import { overrideRuleTooltipDescription } from 'app/features/dashboard/components/PanelEditor/state/getOptionOverrides';

import { DashboardScene } from '../scene/DashboardScene';
import { LibraryPanelBehavior } from '../scene/LibraryPanelBehavior';
import { DashboardGridItem } from '../scene/layout-default/DashboardGridItem';
import { vizPanelToPanel } from '../serialization/transformSceneToSaveModel';
import { activateFullSceneTree } from '../utils/test-utils';
import * as utils from '../utils/utils';

import { PanelOptions } from './PanelOptions';
import { PanelOptionsPane } from './PanelOptionsPane';

const OptionsPaneSelector = selectors.components.PanelEditor.OptionsPane;

standardEditorsRegistry.setInit(getAllOptionEditors);
standardFieldConfigEditorRegistry.setInit(getAllStandardFieldConfigs);

const plugin = getPanelPlugin({
  id: 'TestPanel',
}).useFieldConfig({
  standardOptions: {},
  useCustomConfig: (b) => {
    b.addBooleanSwitch({
      name: 'CustomBool',
      path: 'CustomBool',
    })
      .addBooleanSwitch({
        name: 'HiddenFromDef',
        path: 'HiddenFromDef',
        hideFromDefaults: true,
      })
      .addTextInput({
        name: 'TextPropWithCategory',
        path: 'TextPropWithCategory',
        settings: {
          placeholder: 'CustomTextPropPlaceholder',
        },
        category: ['Axis'],
      });
  },
});

jest.mock('@grafana/runtime', () => ({
  ...jest.requireActual('@grafana/runtime'),
  getPluginImportUtils: () => ({
    getPanelPluginFromCache: jest.fn().mockReturnValue(plugin),
  }),
}));

// Needed when the panel is not part of an DashboardScene
jest.spyOn(utils, 'getDashboardSceneFor').mockReturnValue(new DashboardScene({}));

interface SetupOptions {
  panel?: VizPanel;
}

function setup(options: SetupOptions = {}) {
  let panel = options.panel;

  if (!panel) {
    panel = new VizPanel({
      key: 'panel-1',
      pluginId: 'text',
      title: 'My title',
      fieldConfig: {
        defaults: {},
        overrides: [
          {
            matcher: { id: 'byName', options: 'SeriesA' },
            properties: [
              {
                id: 'decimals',
                value: 2,
              },
            ],
          },
        ],
      },
    });

    new DashboardGridItem({ body: panel });
  }

  // need to wait for plugin to load
  const panelOptionsScene = new PanelOptionsPane({
    panelRef: panel.getRef(),
    searchQuery: '',
    listMode: OptionFilter.All,
  });

  activateFullSceneTree(panelOptionsScene);
  panel.activate();

  const panelOptions = <PanelOptions panel={panel} searchQuery="" listMode={OptionFilter.All}></PanelOptions>;
  const renderResult = render(panelOptions);

  return { renderResult, panelOptionsScene, panel };
}

describe('PanelOptions', () => {
  describe('Can render and edit panel frame options', () => {
    it('Can edit title', async () => {
      const { panel } = setup();

      expect(screen.getByLabelText(OptionsPaneSelector.fieldLabel('Panel options Title'))).toBeInTheDocument();

      const input = screen.getByTestId(selectors.components.PanelEditor.OptionsPane.fieldInput('Title'));
      fireEvent.change(input, { target: { value: 'New title' } });

      expect(panel.state.title).toBe('New title');
    });

    it('Clearing title should set hoverHeader to true', async () => {
      const { panel } = setup();

      expect(screen.getByLabelText(OptionsPaneSelector.fieldLabel('Panel options Title'))).toBeInTheDocument();

      const input = screen.getByTestId(selectors.components.PanelEditor.OptionsPane.fieldInput('Title'));
      fireEvent.change(input, { target: { value: '' } });

      expect(panel.state.title).toBe('');
      expect(panel.state.hoverHeader).toBe(true);

      fireEvent.change(input, { target: { value: 'Muu' } });
      expect(panel.state.hoverHeader).toBe(false);
    });
  });

  describe('Field overrides', () => {
    it('Should be rendered', async () => {
      const {} = setup();

      expect(screen.getByLabelText(overrideRuleTooltipDescription)).toBeInTheDocument();
    });

    it('Can update', async () => {
      const {} = setup();

      await userEvent.click(screen.getByLabelText('Remove property'));

      expect(screen.queryByLabelText(overrideRuleTooltipDescription)).not.toBeInTheDocument();
    });

    it('Can delete rule', async () => {
      const {} = setup();

      await userEvent.click(screen.getByLabelText('Remove override'));

      expect(screen.queryByLabelText(overrideRuleTooltipDescription)).not.toBeInTheDocument();
    });
  });

  it('gets library panel options when the editing a library panel', async () => {
    const panel = new VizPanel({
      key: 'panel-1',
      pluginId: 'text',
    });

    const libraryPanelModel = {
      title: 'title',
      uid: 'uid',
      name: 'libraryPanelName',
      model: vizPanelToPanel(panel),
      type: 'panel',
      version: 1,
    };

    const libraryPanel = new LibraryPanelBehavior({
      isLoaded: true,
      uid: libraryPanelModel.uid,
      name: libraryPanelModel.name,
      _loadedPanel: libraryPanelModel,
    });

    panel.setState({ $behaviors: [libraryPanel] });

    new DashboardGridItem({ body: panel });

    const { renderResult } = setup({ panel: panel });

    const input = await renderResult.findByTestId('library panel name input');

    await act(async () => {
      fireEvent.blur(input, { target: { value: 'new library panel name' } });
    });

    expect((panel.state.$behaviors![0] as LibraryPanelBehavior).state.name).toBe('new library panel name');
  });
});
