import { GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui';
import { GlSingleStat } from '@gitlab/ui/dist/charts';
import { shallowMount } from '@vue/test-utils';
import metricsData from 'test_fixtures/projects/analytics/value_stream_analytics/summary.json';
import waitForPromises from 'helpers/wait_for_promises';
import { METRIC_TYPE_SUMMARY } from '~/api/analytics_api';
import ValueStreamMetrics from '~/cycle_analytics/components/value_stream_metrics.vue';
import createFlash from '~/flash';
import { redirectTo } from '~/lib/utils/url_utility';
import { group } from './mock_data';

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

describe('ValueStreamMetrics', () => {
  let wrapper;
  let mockGetValueStreamSummaryMetrics;

  const { full_path: requestPath } = group;
  const fakeReqName = 'Mock metrics';
  const metricsRequestFactory = () => ({
    request: mockGetValueStreamSummaryMetrics,
    endpoint: METRIC_TYPE_SUMMARY,
    name: fakeReqName,
  });

  const createComponent = ({ requestParams = {} } = {}) => {
    return shallowMount(ValueStreamMetrics, {
      propsData: {
        requestPath,
        requestParams,
        requests: [metricsRequestFactory()],
      },
    });
  };

  const findMetrics = () => wrapper.findAllComponents(GlSingleStat);

  const expectToHaveRequest = (fields) => {
    expect(mockGetValueStreamSummaryMetrics).toHaveBeenCalledWith({
      endpoint: METRIC_TYPE_SUMMARY,
      requestPath,
      ...fields,
    });
  };

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

  describe('with successful requests', () => {
    beforeEach(() => {
      mockGetValueStreamSummaryMetrics = jest.fn().mockResolvedValue({ data: metricsData });
      wrapper = createComponent();
    });

    it('will display a loader with pending requests', async () => {
      await wrapper.vm.$nextTick();

      expect(wrapper.findComponent(GlSkeletonLoading).exists()).toBe(true);
    });

    it('renders hidden GlSingleStat components for each metric', async () => {
      await waitForPromises();

      wrapper.setData({ isLoading: true });

      await wrapper.vm.$nextTick();

      const components = findMetrics();

      expect(components).toHaveLength(metricsData.length);

      metricsData.forEach((metric, index) => {
        expect(components.at(index).isVisible()).toBe(false);
      });
    });

    describe('with data loaded', () => {
      beforeEach(async () => {
        await waitForPromises();
      });

      it('fetches data from the value stream analytics endpoint', () => {
        expectToHaveRequest({ params: {} });
      });

      describe.each`
        index | value                   | title                   | unit                   | clickable
        ${0}  | ${metricsData[0].value} | ${metricsData[0].title} | ${metricsData[0].unit} | ${false}
        ${1}  | ${metricsData[1].value} | ${metricsData[1].title} | ${metricsData[1].unit} | ${false}
        ${2}  | ${metricsData[2].value} | ${metricsData[2].title} | ${metricsData[2].unit} | ${false}
        ${3}  | ${metricsData[3].value} | ${metricsData[3].title} | ${metricsData[3].unit} | ${true}
      `('metric tiles', ({ index, value, title, unit, clickable }) => {
        it(`renders a single stat component for "${title}" with value and unit`, () => {
          const metric = findMetrics().at(index);
          expect(metric.props()).toMatchObject({ value, title, unit: unit ?? '' });
          expect(metric.isVisible()).toBe(true);
        });

        it(`${
          clickable ? 'redirects' : "doesn't redirect"
        } when the user clicks the "${title}" metric`, () => {
          const metric = findMetrics().at(index);
          metric.vm.$emit('click');
          if (clickable) {
            expect(redirectTo).toHaveBeenCalledWith(metricsData[index].links[0].url);
          } else {
            expect(redirectTo).not.toHaveBeenCalled();
          }
        });
      });

      it('will not display a loading icon', () => {
        expect(wrapper.find(GlSkeletonLoading).exists()).toBe(false);
      });

      describe('with additional params', () => {
        beforeEach(async () => {
          wrapper = createComponent({
            requestParams: {
              'project_ids[]': [1],
              created_after: '2020-01-01',
              created_before: '2020-02-01',
            },
          });

          await waitForPromises();
        });

        it('fetches data for the `getValueStreamSummaryMetrics` request', () => {
          expectToHaveRequest({
            params: {
              'project_ids[]': [1],
              created_after: '2020-01-01',
              created_before: '2020-02-01',
            },
          });
        });
      });
    });
  });

  describe('with a request failing', () => {
    beforeEach(async () => {
      mockGetValueStreamSummaryMetrics = jest.fn().mockRejectedValue();
      wrapper = createComponent();

      await waitForPromises();
    });

    it('it should render an error message', () => {
      expect(createFlash).toHaveBeenCalledWith({
        message: `There was an error while fetching value stream analytics ${fakeReqName} data.`,
      });
    });
  });
});
