import { screen, render, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { TestProvider } from 'test/helpers/TestProvider';
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';

import { selectors } from '@grafana/e2e-selectors';
import { LocationServiceProvider, config, locationService } from '@grafana/runtime';
import { SceneQueryRunner, SceneTimeRange, UrlSyncContextProvider, VizPanel } from '@grafana/scenes';
import { playlistSrv } from 'app/features/playlist/PlaylistSrv';
import { DashboardMeta } from 'app/types';

import { buildPanelEditScene } from '../panel-edit/PanelEditor';

import { DashboardScene } from './DashboardScene';
import { ToolbarActions } from './NavToolbarActions';
import { DefaultGridLayoutManager } from './layout-default/DefaultGridLayoutManager';

jest.mock('app/features/playlist/PlaylistSrv', () => ({
  playlistSrv: {
    useState: jest.fn().mockReturnValue({ isPlaying: false }),
    setState: jest.fn(),
    isPlaying: true,
    start: jest.fn(),
    next: jest.fn(),
    prev: jest.fn(),
    stop: jest.fn(),
  },
}));

jest.mock('@grafana/runtime', () => ({
  ...jest.requireActual('@grafana/runtime'),
  getDataSourceSrv: () => ({
    get: jest.fn(),
    getInstanceSettings: jest.fn().mockReturnValue({
      uid: 'datasource-uid',
      name: 'datasource-name',
    }),
  }),
}));

describe('NavToolbarActions', () => {
  describe('Given an already saved dashboard', () => {
    it('Should show correct buttons when not in editing', async () => {
      setup();

      expect(screen.queryByText('Save dashboard')).not.toBeInTheDocument();
      expect(screen.queryByLabelText('Add')).not.toBeInTheDocument();
      expect(await screen.findByText('Edit')).toBeInTheDocument();
      expect(await screen.findByText('Share')).toBeInTheDocument();
    });

    it('Should show the correct buttons when playing a playlist', async () => {
      jest.mocked(playlistSrv).useState.mockReturnValueOnce({ isPlaying: true });
      setup();

      expect(await screen.findByTestId(selectors.pages.Dashboard.DashNav.playlistControls.prev)).toBeInTheDocument();
      expect(await screen.findByTestId(selectors.pages.Dashboard.DashNav.playlistControls.stop)).toBeInTheDocument();
      expect(await screen.findByTestId(selectors.pages.Dashboard.DashNav.playlistControls.next)).toBeInTheDocument();
      expect(screen.queryByText('Edit')).not.toBeInTheDocument();
      expect(screen.queryByText('Share')).not.toBeInTheDocument();
    });

    it('Should call the playlist srv when using playlist controls', async () => {
      jest.mocked(playlistSrv).useState.mockReturnValueOnce({ isPlaying: true });
      setup();

      // Previous dashboard
      expect(await screen.findByTestId(selectors.pages.Dashboard.DashNav.playlistControls.prev)).toBeInTheDocument();
      await userEvent.click(await screen.findByTestId(selectors.pages.Dashboard.DashNav.playlistControls.prev));
      expect(playlistSrv.prev).toHaveBeenCalledTimes(1);

      // Next dashboard
      expect(await screen.findByTestId(selectors.pages.Dashboard.DashNav.playlistControls.next)).toBeInTheDocument();
      await userEvent.click(await screen.findByTestId(selectors.pages.Dashboard.DashNav.playlistControls.next));
      expect(playlistSrv.next).toHaveBeenCalledTimes(1);

      // Stop playlist
      expect(await screen.findByTestId(selectors.pages.Dashboard.DashNav.playlistControls.stop)).toBeInTheDocument();
      await userEvent.click(await screen.findByTestId(selectors.pages.Dashboard.DashNav.playlistControls.stop));
      expect(playlistSrv.stop).toHaveBeenCalledTimes(1);
    });

    it('Should hide the playlist controls when it is not playing', async () => {
      setup();
      expect(screen.queryByText(selectors.pages.Dashboard.DashNav.playlistControls.prev)).not.toBeInTheDocument();
      expect(screen.queryByText(selectors.pages.Dashboard.DashNav.playlistControls.stop)).not.toBeInTheDocument();
      expect(screen.queryByText(selectors.pages.Dashboard.DashNav.playlistControls.next)).not.toBeInTheDocument();
    });

    it('Should show correct buttons when editing', async () => {
      setup();

      await userEvent.click(await screen.findByText('Edit'));

      expect(await screen.findByText('Save dashboard')).toBeInTheDocument();
      expect(await screen.findByText('Exit edit')).toBeInTheDocument();
      expect(await screen.findByText('Add')).toBeInTheDocument();
      expect(screen.queryByText('Edit')).not.toBeInTheDocument();
      expect(screen.queryByText('Share')).not.toBeInTheDocument();
      expect(screen.queryByText(selectors.pages.Dashboard.DashNav.playlistControls.prev)).not.toBeInTheDocument();
      expect(screen.queryByText(selectors.pages.Dashboard.DashNav.playlistControls.stop)).not.toBeInTheDocument();
      expect(screen.queryByText(selectors.pages.Dashboard.DashNav.playlistControls.next)).not.toBeInTheDocument();
    });

    it('Should show correct buttons when in settings menu', async () => {
      setup();

      await userEvent.click(await screen.findByText('Edit'));
      await userEvent.click(await screen.findByText('Settings'));

      expect(await screen.findByText('Save dashboard')).toBeInTheDocument();
      expect(await screen.findByText('Back to dashboard')).toBeInTheDocument();
      expect(screen.queryByText(selectors.pages.Dashboard.DashNav.playlistControls.prev)).not.toBeInTheDocument();
      expect(screen.queryByText(selectors.pages.Dashboard.DashNav.playlistControls.stop)).not.toBeInTheDocument();
      expect(screen.queryByText(selectors.pages.Dashboard.DashNav.playlistControls.next)).not.toBeInTheDocument();
    });

    it('Should show correct buttons when editing a new panel', async () => {
      const { dashboard } = setup();

      await act(() => {
        dashboard.onEnterEditMode();
        const panel = dashboard.state.body.getVizPanels()[0];
        dashboard.setState({ editPanel: buildPanelEditScene(panel, true) });
      });

      expect(await screen.findByText('Save dashboard')).toBeInTheDocument();
      expect(await screen.findByText('Discard panel')).toBeInTheDocument();
      expect(await screen.findByText('Back to dashboard')).toBeInTheDocument();
    });

    it('Should show correct buttons when editing an existing panel', async () => {
      const { dashboard } = setup();

      await act(() => {
        dashboard.onEnterEditMode();
        const panel = dashboard.state.body.getVizPanels()[0];
        dashboard.setState({ editPanel: buildPanelEditScene(panel) });
      });

      expect(await screen.findByText('Save dashboard')).toBeInTheDocument();
      expect(await screen.findByText('Discard panel changes')).toBeInTheDocument();
      expect(await screen.findByText('Back to dashboard')).toBeInTheDocument();
    });
  });

  describe('Given new sharing button', () => {
    it('Should show old share button when newDashboardSharingComponent FF is disabled', async () => {
      setup();

      expect(await screen.findByText('Share')).toBeInTheDocument();
      const newShareButton = screen.queryByTestId(selectors.pages.Dashboard.DashNav.newShareButton.container);
      expect(newShareButton).not.toBeInTheDocument();
      const newExportButton = screen.queryByTestId(selectors.pages.Dashboard.DashNav.NewExportButton.container);
      expect(newExportButton).not.toBeInTheDocument();
    });
    it('Should show new share button when newDashboardSharingComponent FF is enabled', async () => {
      config.featureToggles.newDashboardSharingComponent = true;
      setup();

      expect(await screen.queryByTestId(selectors.pages.Dashboard.DashNav.shareButton)).not.toBeInTheDocument();
      const newShareButton = screen.getByTestId(selectors.pages.Dashboard.DashNav.newShareButton.container);
      expect(newShareButton).toBeInTheDocument();
    });
    it('Should show new export button when newDashboardSharingComponent FF is enabled', async () => {
      config.featureToggles.newDashboardSharingComponent = true;
      setup();
      const newExportButton = screen.getByTestId(selectors.pages.Dashboard.DashNav.NewExportButton.container);
      expect(newExportButton).toBeInTheDocument();
    });
  });

  describe('Snapshot', () => {
    it('should show link button when is a snapshot', () => {
      setup({
        isSnapshot: true,
      });

      expect(screen.queryByTestId('button-snapshot')).toBeInTheDocument();
    });
  });
});

function setup(meta?: DashboardMeta) {
  const dashboard = new DashboardScene({
    $timeRange: new SceneTimeRange({ from: 'now-6h', to: 'now' }),
    meta: {
      canEdit: true,
      isNew: false,
      canMakeEditable: true,
      canSave: true,
      canShare: true,
      canStar: true,
      canAdmin: true,
      canDelete: true,
      ...meta,
    },
    title: 'hello',
    uid: 'dash-1',
    body: DefaultGridLayoutManager.fromVizPanels([
      new VizPanel({
        title: 'Panel A',
        key: 'panel-1',
        pluginId: 'table',
        $data: new SceneQueryRunner({ key: 'data-query-runner', queries: [{ refId: 'A' }] }),
      }),
      new VizPanel({
        title: 'Panel B',
        key: 'panel-2',
        pluginId: 'table',
      }),
    ]),
  });

  const context = getGrafanaContextMock();

  locationService.push('/');

  render(
    <TestProvider grafanaContext={context}>
      <LocationServiceProvider service={locationService}>
        <UrlSyncContextProvider scene={dashboard}>
          <ToolbarActions dashboard={dashboard} />
        </UrlSyncContextProvider>
      </LocationServiceProvider>
    </TestProvider>
  );

  const actions = context.chrome.state.getValue().actions;

  return { dashboard, actions };
}
