import { shallowMount } from '@vue/test-utils';
import { GlDropdownItem } from '@gitlab/ui';
import { createStore } from '~/monitoring/stores';
import { DASHBOARD_PAGE, PANEL_NEW_PAGE } from '~/monitoring/router/constants';
import { setupAllDashboards, setupStoreWithData } from '../store_utils';
import { redirectTo } from '~/lib/utils/url_utility';
import Tracking from '~/tracking';
import ActionsMenu from '~/monitoring/components/dashboard_actions_menu.vue';
import CustomMetricsFormFields from '~/custom_metrics/components/custom_metrics_form_fields.vue';
import { dashboardActionsMenuProps, dashboardGitResponse } from '../mock_data';
import * as types from '~/monitoring/stores/mutation_types';

jest.mock('~/lib/utils/url_utility', () => ({
  redirectTo: jest.fn(),
  queryToObject: jest.fn(),
}));

describe('Actions menu', () => {
  const ootbDashboards = [dashboardGitResponse[0], dashboardGitResponse[2]];
  const customDashboard = dashboardGitResponse[1];

  let store;
  let wrapper;

  const findAddMetricItem = () => wrapper.find('[data-testid="add-metric-item"]');
  const findAddPanelItemEnabled = () => wrapper.find('[data-testid="add-panel-item-enabled"]');
  const findAddPanelItemDisabled = () => wrapper.find('[data-testid="add-panel-item-disabled"]');
  const findAddMetricModal = () => wrapper.find('[data-testid="add-metric-modal"]');
  const findAddMetricModalSubmitButton = () =>
    wrapper.find('[data-testid="add-metric-modal-submit-button"]');
  const findStarDashboardItem = () => wrapper.find('[data-testid="star-dashboard-item"]');
  const findEditDashboardItemEnabled = () =>
    wrapper.find('[data-testid="edit-dashboard-item-enabled"]');
  const findEditDashboardItemDisabled = () =>
    wrapper.find('[data-testid="edit-dashboard-item-disabled"]');
  const findDuplicateDashboardItem = () => wrapper.find('[data-testid="duplicate-dashboard-item"]');
  const findDuplicateDashboardModal = () =>
    wrapper.find('[data-testid="duplicate-dashboard-modal"]');
  const findCreateDashboardItem = () => wrapper.find('[data-testid="create-dashboard-item"]');
  const findCreateDashboardModal = () => wrapper.find('[data-testid="create-dashboard-modal"]');

  const createShallowWrapper = (props = {}, options = {}) => {
    wrapper = shallowMount(ActionsMenu, {
      propsData: { ...dashboardActionsMenuProps, ...props },
      store,
      ...options,
    });
  };

  beforeEach(() => {
    store = createStore();
  });

  afterEach(() => {
    wrapper.destroy();
    wrapper = null;
  });

  describe('add metric item', () => {
    it('is rendered when custom metrics are available', () => {
      createShallowWrapper();

      return wrapper.vm.$nextTick(() => {
        expect(findAddMetricItem().exists()).toBe(true);
      });
    });

    it('is not rendered when custom metrics are not available', () => {
      createShallowWrapper({
        addingMetricsAvailable: false,
      });

      return wrapper.vm.$nextTick(() => {
        expect(findAddMetricItem().exists()).toBe(false);
      });
    });

    describe('when available', () => {
      beforeEach(() => {
        createShallowWrapper();
      });

      it('modal for custom metrics form is rendered', () => {
        expect(findAddMetricModal().exists()).toBe(true);
        expect(findAddMetricModal().attributes().modalid).toBe('addMetric');
      });

      it('add metric modal submit button exists', () => {
        expect(findAddMetricModalSubmitButton().exists()).toBe(true);
      });

      it('renders custom metrics form fields', () => {
        expect(wrapper.find(CustomMetricsFormFields).exists()).toBe(true);
      });
    });

    describe('when not available', () => {
      beforeEach(() => {
        createShallowWrapper({ addingMetricsAvailable: false });
      });

      it('modal for custom metrics form is not rendered', () => {
        expect(findAddMetricModal().exists()).toBe(false);
      });
    });

    describe('adding new metric from modal', () => {
      let origPage;

      beforeEach(done => {
        jest.spyOn(Tracking, 'event').mockReturnValue();
        createShallowWrapper();

        setupStoreWithData(store);

        origPage = document.body.dataset.page;
        document.body.dataset.page = 'projects:environments:metrics';

        wrapper.vm.$nextTick(done);
      });

      afterEach(() => {
        document.body.dataset.page = origPage;
      });

      it('is tracked', done => {
        const submitButton = findAddMetricModalSubmitButton().vm;

        wrapper.vm.$nextTick(() => {
          submitButton.$el.click();
          wrapper.vm.$nextTick(() => {
            expect(Tracking.event).toHaveBeenCalledWith(
              document.body.dataset.page,
              'click_button',
              {
                label: 'add_new_metric',
                property: 'modal',
                value: undefined,
              },
            );
            done();
          });
        });
      });
    });
  });

  describe('add panel item', () => {
    const GlDropdownItemStub = {
      extends: GlDropdownItem,
      props: {
        to: [String, Object],
      },
    };

    let $route;

    beforeEach(() => {
      $route = { name: DASHBOARD_PAGE, params: { dashboard: 'my_dashboard.yml' } };

      createShallowWrapper(
        {
          isOotbDashboard: false,
        },
        {
          mocks: { $route },
          stubs: { GlDropdownItem: GlDropdownItemStub },
        },
      );
    });

    it('is disabled for ootb dashboards', () => {
      createShallowWrapper({
        isOotbDashboard: true,
      });

      return wrapper.vm.$nextTick(() => {
        expect(findAddPanelItemDisabled().exists()).toBe(true);
      });
    });

    it('is visible for custom dashboards', () => {
      expect(findAddPanelItemEnabled().exists()).toBe(true);
    });

    it('renders a link to the new panel page for custom dashboards', () => {
      expect(findAddPanelItemEnabled().props('to')).toEqual({
        name: PANEL_NEW_PAGE,
        params: {
          dashboard: 'my_dashboard.yml',
        },
      });
    });
  });

  describe('edit dashboard yml item', () => {
    beforeEach(() => {
      createShallowWrapper();
    });

    describe('when current dashboard is custom', () => {
      beforeEach(() => {
        setupAllDashboards(store, customDashboard.path);
      });

      it('enabled item is rendered and has falsy disabled attribute', () => {
        expect(findEditDashboardItemEnabled().exists()).toBe(true);
        expect(findEditDashboardItemEnabled().attributes('disabled')).toBe(undefined);
      });

      it('enabled item links to their edit path', () => {
        expect(findEditDashboardItemEnabled().attributes('href')).toBe(
          customDashboard.project_blob_path,
        );
      });

      it('disabled item is not rendered', () => {
        expect(findEditDashboardItemDisabled().exists()).toBe(false);
      });
    });

    describe.each(ootbDashboards)('when current dashboard is OOTB', dashboard => {
      beforeEach(() => {
        setupAllDashboards(store, dashboard.path);
      });

      it('disabled item is rendered and has disabled attribute set on it', () => {
        expect(findEditDashboardItemDisabled().exists()).toBe(true);
        expect(findEditDashboardItemDisabled().attributes('disabled')).toBe('');
      });

      it('enabled item is not rendered', () => {
        expect(findEditDashboardItemEnabled().exists()).toBe(false);
      });
    });
  });

  describe('duplicate dashboard item', () => {
    beforeEach(() => {
      createShallowWrapper();
    });

    describe.each(ootbDashboards)('when current dashboard is OOTB', dashboard => {
      beforeEach(() => {
        setupAllDashboards(store, dashboard.path);
      });

      it('is rendered', () => {
        expect(findDuplicateDashboardItem().exists()).toBe(true);
      });

      it('duplicate dashboard modal is rendered', () => {
        expect(findDuplicateDashboardModal().exists()).toBe(true);
      });

      it('clicking on item opens up the duplicate dashboard modal', () => {
        const modalId = 'duplicateDashboard';
        const modalTrigger = findDuplicateDashboardItem();
        const rootEmit = jest.spyOn(wrapper.vm.$root, '$emit');

        modalTrigger.trigger('click');

        return wrapper.vm.$nextTick().then(() => {
          expect(rootEmit.mock.calls[0]).toContainEqual(modalId);
        });
      });
    });

    describe('when current dashboard is custom', () => {
      beforeEach(() => {
        setupAllDashboards(store, customDashboard.path);
      });

      it('is not rendered', () => {
        expect(findDuplicateDashboardItem().exists()).toBe(false);
      });

      it('duplicate dashboard modal is not rendered', () => {
        expect(findDuplicateDashboardModal().exists()).toBe(false);
      });
    });

    describe('when no dashboard is set', () => {
      it('is not rendered', () => {
        expect(findDuplicateDashboardItem().exists()).toBe(false);
      });

      it('duplicate dashboard modal is not rendered', () => {
        expect(findDuplicateDashboardModal().exists()).toBe(false);
      });
    });

    describe('when a dashboard has been duplicated in the duplicate dashboard modal', () => {
      beforeEach(() => {
        store.state.monitoringDashboard.projectPath = 'root/sandbox';

        setupAllDashboards(store, dashboardGitResponse[0].path);
      });

      it('redirects to the newly created dashboard', () => {
        delete window.location;
        window.location = new URL('https://localhost');

        const newDashboard = dashboardGitResponse[1];

        const newDashboardUrl = 'root/sandbox/-/metrics/dashboard.yml';
        findDuplicateDashboardModal().vm.$emit('dashboardDuplicated', newDashboard);

        return wrapper.vm.$nextTick().then(() => {
          expect(redirectTo).toHaveBeenCalled();
          expect(redirectTo).toHaveBeenCalledWith(newDashboardUrl);
        });
      });
    });
  });

  describe('star dashboard item', () => {
    beforeEach(() => {
      createShallowWrapper();
      setupAllDashboards(store);

      jest.spyOn(store, 'dispatch').mockResolvedValue();
    });

    it('is shown', () => {
      expect(findStarDashboardItem().exists()).toBe(true);
    });

    it('is not disabled', () => {
      expect(findStarDashboardItem().attributes('disabled')).toBeFalsy();
    });

    it('is disabled when starring is taking place', () => {
      store.commit(`monitoringDashboard/${types.REQUEST_DASHBOARD_STARRING}`);

      return wrapper.vm.$nextTick(() => {
        expect(findStarDashboardItem().exists()).toBe(true);
        expect(findStarDashboardItem().attributes('disabled')).toBe('true');
      });
    });

    it('on click it dispatches a toggle star action', () => {
      findStarDashboardItem().vm.$emit('click');

      return wrapper.vm.$nextTick().then(() => {
        expect(store.dispatch).toHaveBeenCalledWith(
          'monitoringDashboard/toggleStarredValue',
          undefined,
        );
      });
    });

    describe('when dashboard is not starred', () => {
      beforeEach(() => {
        store.commit(`monitoringDashboard/${types.SET_INITIAL_STATE}`, {
          currentDashboard: dashboardGitResponse[0].path,
        });
        return wrapper.vm.$nextTick();
      });

      it('item text shows "Star dashboard"', () => {
        expect(findStarDashboardItem().html()).toMatch(/Star dashboard/);
      });
    });

    describe('when dashboard is starred', () => {
      beforeEach(() => {
        store.commit(`monitoringDashboard/${types.SET_INITIAL_STATE}`, {
          currentDashboard: dashboardGitResponse[1].path,
        });
        return wrapper.vm.$nextTick();
      });

      it('item text shows "Unstar dashboard"', () => {
        expect(findStarDashboardItem().html()).toMatch(/Unstar dashboard/);
      });
    });
  });

  describe('create dashboard item', () => {
    beforeEach(() => {
      createShallowWrapper();
    });

    it('is rendered by default but it is disabled', () => {
      expect(findCreateDashboardItem().attributes('disabled')).toBe('true');
    });

    describe('when project path is set', () => {
      const mockProjectPath = 'root/sandbox';
      const mockAddDashboardDocPath = '/doc/add-dashboard';

      beforeEach(() => {
        store.state.monitoringDashboard.projectPath = mockProjectPath;
        store.state.monitoringDashboard.addDashboardDocumentationPath = mockAddDashboardDocPath;
      });

      it('is not disabled', () => {
        expect(findCreateDashboardItem().attributes('disabled')).toBe(undefined);
      });

      it('renders a modal for creating a dashboard', () => {
        expect(findCreateDashboardModal().exists()).toBe(true);
      });

      it('clicking opens up the modal', () => {
        const modalId = 'createDashboard';
        const modalTrigger = findCreateDashboardItem();
        const rootEmit = jest.spyOn(wrapper.vm.$root, '$emit');

        modalTrigger.trigger('click');

        return wrapper.vm.$nextTick().then(() => {
          expect(rootEmit.mock.calls[0]).toContainEqual(modalId);
        });
      });

      it('modal gets passed correct props', () => {
        expect(findCreateDashboardModal().props('projectPath')).toBe(mockProjectPath);
        expect(findCreateDashboardModal().props('addDashboardDocumentationPath')).toBe(
          mockAddDashboardDocPath,
        );
      });
    });

    describe('when project path is not set', () => {
      beforeEach(() => {
        store.state.monitoringDashboard.projectPath = null;
      });

      it('is disabled', () => {
        expect(findCreateDashboardItem().attributes('disabled')).toBe('true');
      });

      it('does not render a modal for creating a dashboard', () => {
        expect(findCreateDashboardModal().exists()).toBe(false);
      });
    });
  });
});
