import { config, locationService } from '@grafana/runtime';
import { CustomVariable, UrlSyncManager } from '@grafana/scenes';
import { DashboardDataDTO } from 'app/types';

import { transformSaveModelToScene } from '../serialization/transformSaveModelToScene';

import { PRESERVED_SCENE_STATE_KEY, restoreDashboardStateFromLocalStorage } from './dashboardSessionState';

describe('dashboardSessionState', () => {
  beforeAll(() => {
    config.featureToggles.preserveDashboardStateWhenNavigating = true;
  });

  afterAll(() => {
    config.featureToggles.preserveDashboardStateWhenNavigating = false;
  });

  beforeEach(() => {});

  describe('behavior', () => {
    it('should do nothing for default home dashboard', () => {
      const scene = buildTestScene();
      scene.setState({ uid: undefined });

      const deactivate = scene.activate();
      expect(window.sessionStorage.getItem(PRESERVED_SCENE_STATE_KEY)).toBeNull();

      deactivate();
      expect(window.sessionStorage.getItem(PRESERVED_SCENE_STATE_KEY)).toBeNull();
    });

    it('should capture dashboard scene state and save it to session storage on deactivation', () => {
      const scene = buildTestScene();

      const deactivate = scene.activate();
      expect(window.sessionStorage.getItem(PRESERVED_SCENE_STATE_KEY)).toBeNull();

      deactivate();
      expect(window.sessionStorage.getItem(PRESERVED_SCENE_STATE_KEY)).toBe(
        '?from=now-6h&to=now&timezone=browser&var-customVar=a'
      );
    });
  });

  describe('restoreDashboardStateFromLocalStorage', () => {
    it('should restore dashboard state from session storage', () => {
      window.sessionStorage.setItem(PRESERVED_SCENE_STATE_KEY, '?var-customVar=b&from=now-5m&to=now&timezone=browser');
      const scene = buildTestScene();

      restoreDashboardStateFromLocalStorage(scene);
      const variable = scene.state.$variables!.getByName('customVar') as CustomVariable;
      const timeRange = scene.state.$timeRange;

      const urlSyncManager = new UrlSyncManager();
      urlSyncManager.initSync(scene);

      expect(variable!.state!.value).toEqual(['b']);
      expect(variable!.state!.text).toEqual(['b']);
      expect(timeRange?.state.from).toEqual('now-5m');
      expect(timeRange?.state.to).toEqual('now');
    });

    it('should remove query params that are not applicable on a target dashboard', () => {
      window.sessionStorage.setItem(
        PRESERVED_SCENE_STATE_KEY,
        '?var-customVar=b&var-nonApplicableVar=b&from=now-5m&to=now&timezone=browser'
      );

      const scene = buildTestScene();

      restoreDashboardStateFromLocalStorage(scene);

      expect(locationService.getLocation().search).toBe('?var-customVar=b&from=now-5m&to=now&timezone=browser');
    });

    // handles case when user navigates back to a dashboard with the same state, i.e. using back button
    it('should remove duplicate query params', () => {
      locationService.replace({ search: 'var-customVar=b&from=now-6h&to=now&timezone=browser' });

      window.sessionStorage.setItem(PRESERVED_SCENE_STATE_KEY, '?var-customVar=b&from=now-5m&to=now&timezone=browser');
      const scene = buildTestScene();

      restoreDashboardStateFromLocalStorage(scene);

      expect(locationService.getLocation().search).toBe('?var-customVar=b&from=now-6h&to=now&timezone=browser');
    });
  });
});

function buildTestScene() {
  const testDashboard: DashboardDataDTO = {
    annotations: { list: [] },
    editable: true,
    fiscalYearStartMonth: 0,
    graphTooltip: 0,
    id: 2483,
    links: [],
    panels: [],
    schemaVersion: 39,
    tags: [],
    templating: {
      list: [
        {
          multi: true,
          name: 'customVar',
          query: 'a,b,c',
          type: 'custom',
        },
      ],
    },
    time: {
      from: 'now-6h',
      to: 'now',
    },
    timepicker: {},
    timezone: 'browser',
    title: 'Test dashboard',
    uid: 'edhmd9stpd6o0a',
    version: 24,
    weekStart: '',
  };

  const scene = transformSaveModelToScene({ dashboard: testDashboard, meta: {} });

  // Removing data layers to avoid mocking built-in Grafana data source
  scene.setState({ $data: undefined });

  return scene;
}
