import { GlTabs, GlTab } from '@gitlab/ui';
import { merge } from 'lodash';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import setWindowLocation from 'helpers/set_window_location_helper';
import { TEST_HOST } from 'helpers/test_constants';
import { mergeUrlParams, updateHistory, getParameterValues } from '~/lib/utils/url_utility';
import Component from '~/projects/pipelines/charts/components/app.vue';
import PipelineCharts from '~/projects/pipelines/charts/components/pipeline_charts.vue';
import API from '~/api';

jest.mock('~/lib/utils/url_utility');

const DeploymentFrequencyChartsStub = { name: 'DeploymentFrequencyCharts', render: () => {} };
const LeadTimeChartsStub = { name: 'LeadTimeCharts', render: () => {} };
const ProjectQualitySummaryStub = { name: 'ProjectQualitySummary', render: () => {} };

describe('ProjectsPipelinesChartsApp', () => {
  let wrapper;

  function createComponent(mountOptions = {}) {
    wrapper = shallowMountExtended(
      Component,
      merge(
        {},
        {
          provide: {
            shouldRenderDoraCharts: true,
            shouldRenderQualitySummary: true,
          },
          stubs: {
            DeploymentFrequencyCharts: DeploymentFrequencyChartsStub,
            LeadTimeCharts: LeadTimeChartsStub,
            ProjectQualitySummary: ProjectQualitySummaryStub,
          },
        },
        mountOptions,
      ),
    );
  }

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

  const findGlTabs = () => wrapper.find(GlTabs);
  const findAllGlTabs = () => wrapper.findAll(GlTab);
  const findGlTabAtIndex = (index) => findAllGlTabs().at(index);
  const findLeadTimeCharts = () => wrapper.find(LeadTimeChartsStub);
  const findDeploymentFrequencyCharts = () => wrapper.find(DeploymentFrequencyChartsStub);
  const findPipelineCharts = () => wrapper.find(PipelineCharts);
  const findProjectQualitySummary = () => wrapper.find(ProjectQualitySummaryStub);

  describe('when all charts are available', () => {
    beforeEach(() => {
      createComponent();
    });

    it('renders tabs', () => {
      expect(findGlTabs().exists()).toBe(true);

      expect(findGlTabAtIndex(0).attributes('title')).toBe('Pipelines');
      expect(findGlTabAtIndex(1).attributes('title')).toBe('Deployment frequency');
      expect(findGlTabAtIndex(2).attributes('title')).toBe('Lead time');
    });

    it('renders the pipeline charts', () => {
      expect(findPipelineCharts().exists()).toBe(true);
    });

    it('renders the deployment frequency charts', () => {
      expect(findDeploymentFrequencyCharts().exists()).toBe(true);
    });

    it('renders the lead time charts', () => {
      expect(findLeadTimeCharts().exists()).toBe(true);
    });

    it('renders the project quality summary', () => {
      expect(findProjectQualitySummary().exists()).toBe(true);
    });

    it('sets the tab and url when a tab is clicked', async () => {
      let chartsPath;
      setWindowLocation(`${TEST_HOST}/gitlab-org/gitlab-test/-/pipelines/charts`);

      mergeUrlParams.mockImplementation(({ chart }, path) => {
        expect(chart).toBe('deployment-frequency');
        expect(path).toBe(window.location.pathname);
        chartsPath = `${path}?chart=${chart}`;
        return chartsPath;
      });

      updateHistory.mockImplementation(({ url }) => {
        expect(url).toBe(chartsPath);
      });
      const tabs = findGlTabs();

      expect(tabs.attributes('value')).toBe('0');

      tabs.vm.$emit('input', 1);

      await wrapper.vm.$nextTick();

      expect(tabs.attributes('value')).toBe('1');
    });

    it('should not try to push history if the tab does not change', async () => {
      setWindowLocation(`${TEST_HOST}/gitlab-org/gitlab-test/-/pipelines/charts`);

      mergeUrlParams.mockImplementation(({ chart }, path) => `${path}?chart=${chart}`);

      const tabs = findGlTabs();

      expect(tabs.attributes('value')).toBe('0');

      tabs.vm.$emit('input', 0);

      await wrapper.vm.$nextTick();

      expect(updateHistory).not.toHaveBeenCalled();
    });

    describe('event tracking', () => {
      it.each`
        testId                        | event
        ${'pipelines-tab'}            | ${'p_analytics_ci_cd_pipelines'}
        ${'deployment-frequency-tab'} | ${'p_analytics_ci_cd_deployment_frequency'}
        ${'lead-time-tab'}            | ${'p_analytics_ci_cd_lead_time'}
      `('tracks the $event event when clicked', ({ testId, event }) => {
        jest.spyOn(API, 'trackRedisHllUserEvent');

        expect(API.trackRedisHllUserEvent).not.toHaveBeenCalled();

        wrapper.findByTestId(testId).vm.$emit('click');

        expect(API.trackRedisHllUserEvent).toHaveBeenCalledWith(event);
      });
    });
  });

  describe('when provided with a query param', () => {
    it.each`
      chart                     | tab
      ${'lead-time'}            | ${'2'}
      ${'deployment-frequency'} | ${'1'}
      ${'pipelines'}            | ${'0'}
      ${'fake'}                 | ${'0'}
      ${''}                     | ${'0'}
    `('shows the correct tab for URL parameter "$chart"', ({ chart, tab }) => {
      setWindowLocation(`${TEST_HOST}/gitlab-org/gitlab-test/-/pipelines/charts?chart=${chart}`);
      getParameterValues.mockImplementation((name) => {
        expect(name).toBe('chart');
        return chart ? [chart] : [];
      });
      createComponent();
      expect(findGlTabs().attributes('value')).toBe(tab);
    });

    it('should set the tab when the back button is clicked', async () => {
      let popstateHandler;

      window.addEventListener = jest.fn();

      window.addEventListener.mockImplementation((event, handler) => {
        if (event === 'popstate') {
          popstateHandler = handler;
        }
      });

      getParameterValues.mockImplementation((name) => {
        expect(name).toBe('chart');
        return [];
      });

      createComponent();

      expect(findGlTabs().attributes('value')).toBe('0');

      getParameterValues.mockImplementationOnce((name) => {
        expect(name).toBe('chart');
        return ['deployment-frequency'];
      });

      popstateHandler();

      await wrapper.vm.$nextTick();

      expect(findGlTabs().attributes('value')).toBe('1');
    });
  });

  describe('when the dora charts are not available and project quality summary is not available', () => {
    beforeEach(() => {
      createComponent({
        provide: { shouldRenderDoraCharts: false, shouldRenderQualitySummary: false },
      });
    });

    it('does not render tabs', () => {
      expect(findGlTabs().exists()).toBe(false);
    });

    it('renders the pipeline charts', () => {
      expect(findPipelineCharts().exists()).toBe(true);
    });
  });

  describe('when the project quality summary is not available', () => {
    beforeEach(() => {
      createComponent({ provide: { shouldRenderQualitySummary: false } });
    });

    it('does not render the tab', () => {
      expect(findProjectQualitySummary().exists()).toBe(false);
    });
  });
});