import { nextTick } from 'vue';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import Cookies from 'js-cookie';
import VueRouter from 'vue-router';
import ProjectVulnerabilityReport from 'ee/security_dashboard/components/project/project_vulnerability_report.vue';
import ReportNotConfiguredProject from 'ee/security_dashboard/components/shared/empty_states/report_not_configured_project.vue';
import VulnerabilityReportTabs from 'ee/security_dashboard/components/shared/vulnerability_report/vulnerability_report_tabs.vue';
import ProjectPipelineStatus from 'ee/security_dashboard/components/shared/project_pipeline_status.vue';
import SecurityScannerAlert from 'ee/security_dashboard/components/project/security_scanner_alert.vue';
import securityScannersQuery from 'ee/security_dashboard/graphql/queries/project_security_scanners.query.graphql';
import AutoFixUserCallout from 'ee/security_dashboard/components/shared/auto_fix_user_callout.vue';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import createMockApollo from 'helpers/mock_apollo_helper';

const localVue = createLocalVue();
localVue.use(VueApollo);
localVue.use(VueRouter);
const router = new VueRouter();

describe('Project vulnerability report app component', () => {
  useLocalStorageSpy();

  let wrapper;

  const securityScannersHandler = ({ available = [], enabled = [], pipelineRun = [] } = {}) =>
    jest.fn().mockResolvedValue({
      data: {
        project: {
          id: 1,
          securityScanners: { available, enabled, pipelineRun },
        },
      },
    });

  const createWrapper = ({
    pipeline = { id: 1 },
    securityScanners,
    securityAutoFix = false,
  } = {}) => {
    wrapper = shallowMount(ProjectVulnerabilityReport, {
      localVue,
      router,
      apolloProvider: createMockApollo([
        [securityScannersQuery, securityScannersHandler(securityScanners)],
      ]),
      provide: {
        fullPath: '#',
        autoFixDocumentation: '#',
        pipeline,
        glFeatures: { securityAutoFix },
      },
      stubs: {
        VulnerabilityReportTabs,
        LocalStorageSync,
      },
    });
  };

  const findReportNotConfiguredProject = () => wrapper.find(ReportNotConfiguredProject);
  const findVulnerabilityReportTabs = () => wrapper.findComponent(VulnerabilityReportTabs);
  const findAutoFixUserCallout = () => wrapper.findComponent(AutoFixUserCallout);
  const findProjectPipelineStatus = () => wrapper.findComponent(ProjectPipelineStatus);
  const findSecurityScannerAlert = (root = wrapper) => root.findComponent(SecurityScannerAlert);

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

  describe('report not configured component', () => {
    it('shows the report not configured component if there are no projects', () => {
      createWrapper({ pipeline: null });

      expect(findReportNotConfiguredProject().exists()).toBe(true);
      expect(findVulnerabilityReportTabs().exists()).toBe(false);
    });

    it('shows the vulnerability report tabs and project pipeline status components if there are projects', () => {
      const pipeline = { id: 1 };
      createWrapper({ pipeline });

      expect(findReportNotConfiguredProject().exists()).toBe(false);
      expect(findVulnerabilityReportTabs().exists()).toBe(true);
      expect(findProjectPipelineStatus().props('pipeline')).toBe(pipeline);
    });
  });

  describe('security scanner alerts component', () => {
    describe.each`
      available   | enabled     | pipelineRun | expectAlertShown
      ${['DAST']} | ${[]}       | ${[]}       | ${true}
      ${['DAST']} | ${['DAST']} | ${[]}       | ${true}
      ${['DAST']} | ${[]}       | ${['DAST']} | ${true}
      ${['DAST']} | ${['DAST']} | ${['DAST']} | ${false}
      ${[]}       | ${[]}       | ${[]}       | ${false}
    `('visibility', ({ available, enabled, pipelineRun, expectAlertShown }) => {
      it(`should${expectAlertShown ? '' : ' not'} show the alert`, async () => {
        createWrapper({ securityScanners: { available, enabled, pipelineRun } });
        await nextTick();

        expect(findSecurityScannerAlert().exists()).toBe(expectAlertShown);
      });

      it('should never show the alert once it has been dismissed', () => {
        window.localStorage.setItem(
          ProjectVulnerabilityReport.SCANNER_ALERT_DISMISSED_LOCAL_STORAGE_KEY,
          'true',
        );

        createWrapper({ securityScanners: { available, enabled, pipelineRun } });

        expect(findSecurityScannerAlert().exists()).toBe(false);
      });
    });

    describe('dismissal', () => {
      beforeEach(() => {
        createWrapper({
          securityScanners: { available: ['DAST'], enabled: [], pipelineRun: [] },
        });
      });

      it('should hide the alert when it is dismissed', async () => {
        const scannerAlert = findSecurityScannerAlert();
        expect(scannerAlert.exists()).toBe(true);

        scannerAlert.vm.$emit('dismiss');
        await nextTick();

        expect(scannerAlert.exists()).toBe(false);
      });

      it('should remember the dismissal state', async () => {
        findSecurityScannerAlert().vm.$emit('dismiss');
        await nextTick();

        expect(window.localStorage.setItem).toHaveBeenCalledWith(
          ProjectVulnerabilityReport.SCANNER_ALERT_DISMISSED_LOCAL_STORAGE_KEY,
          'true',
        );
      });
    });
  });

  describe('auto fix user callout component', () => {
    it('does not show user callout when feature flag is disabled', () => {
      createWrapper({ securityAutoFix: false });
      expect(findAutoFixUserCallout().exists()).toBe(false);
    });

    it('shows user callout when the cookie is not set and hides it when dismissed', async () => {
      jest.spyOn(Cookies, 'set');
      createWrapper({ securityAutoFix: true });
      const autoFixUserCallOut = findAutoFixUserCallout();

      expect(autoFixUserCallOut.exists()).toBe(true);

      autoFixUserCallOut.vm.$emit('close');
      await nextTick();

      expect(autoFixUserCallOut.exists()).toBe(false);
      expect(Cookies.set).toHaveBeenCalledWith(
        wrapper.vm.$options.autoFixUserCalloutCookieName,
        'true',
      );
    });

    it('does not show user callout when the cookie is set', () => {
      jest.doMock('js-cookie', () => ({ get: jest.fn().mockReturnValue(true) }));
      createWrapper({ securityAutoFix: true });

      expect(findAutoFixUserCallout().exists()).toBe(false);
    });
  });
});
