import { GlLoadingIcon } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Cookies from 'js-cookie';
import { PortalTarget } from 'portal-vue';
import { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import GroupVulnerabilities from 'ee/security_dashboard/components/group/group_vulnerabilities.vue';
import InstanceVulnerabilities from 'ee/security_dashboard/components/instance/instance_vulnerabilities.vue';
import ProjectVulnerabilities from 'ee/security_dashboard/components/project/project_vulnerabilities.vue';
import AutoFixUserCallout from 'ee/security_dashboard/components/shared/auto_fix_user_callout.vue';
import CsvExportButton from 'ee/security_dashboard/components/shared/csv_export_button.vue';
import DashboardNotConfiguredGroup from 'ee/security_dashboard/components/shared/empty_states/group_dashboard_not_configured.vue';
import DashboardNotConfiguredInstance from 'ee/security_dashboard/components/shared/empty_states/instance_dashboard_not_configured.vue';
import DashboardNotConfiguredProject from 'ee/security_dashboard/components/shared/empty_states/reports_not_configured.vue';
import Filters from 'ee/security_dashboard/components/shared/filters/filters_layout.vue';
import ProjectPipelineStatus from 'ee/security_dashboard/components/shared/project_pipeline_status.vue';
import SurveyRequestBanner from 'ee/security_dashboard/components/shared/survey_request_banner.vue';
import VulnerabilitiesCountList from 'ee/security_dashboard/components/shared/vulnerability_count_list.vue';
import VulnerabilityReport from 'ee/security_dashboard/components/shared/vulnerability_report.vue';
import VulnerabilityReportLayout from 'ee/security_dashboard/components/shared/vulnerability_report_layout.vue';
import groupProjectsQuery from 'ee/security_dashboard/graphql/queries/group_projects.query.graphql';
import instanceProjectsQuery from 'ee/security_dashboard/graphql/queries/instance_projects.query.graphql';
import { DASHBOARD_TYPES } from 'ee/security_dashboard/store/constants';
import createApolloProvider from 'helpers/mock_apollo_helper';
import { mockVulnerableProjectsInstance, mockVulnerableProjectsGroup } from '../../mock_data';

describe('Vulnerability Report', () => {
  let wrapper;

  const findAlertsPortalTarget = () => wrapper.findComponent(PortalTarget);
  const findSurveyRequestBanner = () => wrapper.findComponent(SurveyRequestBanner);
  const findInstanceVulnerabilities = () => wrapper.findComponent(InstanceVulnerabilities);
  const findGroupVulnerabilities = () => wrapper.findComponent(GroupVulnerabilities);
  const findProjectVulnerabilities = () => wrapper.findComponent(ProjectVulnerabilities);
  const findCsvExportButton = () => wrapper.findComponent(CsvExportButton);
  const findGroupEmptyState = () => wrapper.findComponent(DashboardNotConfiguredGroup);
  const findInstanceEmptyState = () => wrapper.findComponent(DashboardNotConfiguredInstance);
  const findProjectEmptyState = () => wrapper.findComponent(DashboardNotConfiguredProject);
  const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
  const findFilters = () => wrapper.findComponent(Filters);
  const findVulnerabilitiesCountList = () => wrapper.findComponent(VulnerabilitiesCountList);
  const findProjectPipelineStatus = () => wrapper.findComponent(ProjectPipelineStatus);
  const findAutoFixUserCallout = () => wrapper.findComponent(AutoFixUserCallout);

  const findHeader = () => wrapper.find('h2');

  const createWrapper = ({ data = {}, mocks, propsData, provide, apolloProvider }) => {
    const localVue = createLocalVue();

    if (apolloProvider) {
      localVue.use(VueApollo);
    }

    return shallowMount(VulnerabilityReport, {
      localVue,
      apolloProvider,
      data: () => data,
      mocks,
      propsData,
      provide: {
        ...provide,
      },
      stubs: { VulnerabilityReportLayout },
    });
  };

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

  describe('when initialized - all levels', () => {
    beforeEach(() => {
      wrapper = createWrapper({
        provide: {
          dashboardType: DASHBOARD_TYPES.INSTANCE,
        },
        apolloProvider: createApolloProvider([
          [instanceProjectsQuery, jest.fn().mockResolvedValue(mockVulnerableProjectsInstance())],
        ]),
      });
    });

    it('renders the alerts portal target', () => {
      const portalTarget = findAlertsPortalTarget();
      expect(portalTarget.exists()).toBe(true);
      expect(portalTarget.props('name')).toBe(VulnerabilityReport.vulnerabilityReportAlertsPortal);
    });

    it('should show the header', () => {
      expect(findHeader().exists()).toBe(true);
    });

    it('has filters', () => {
      expect(findFilters().exists()).toBe(true);
    });

    it('responds to the filterChange event', async () => {
      const filters = { severity: 'critical' };
      findFilters().vm.$listeners.filterChange(filters);
      await nextTick();

      expect(findInstanceVulnerabilities().props('filters')).toBe(filters);
    });

    it('displays the csv export button', () => {
      expect(findCsvExportButton().exists()).toBe(true);
    });

    it('should show the survey request banner', () => {
      expect(findSurveyRequestBanner().exists()).toBe(true);
    });
  });

  describe('when initialized - instance level', () => {
    const filters = {};

    beforeEach(() => {
      wrapper = createWrapper({
        provide: {
          dashboardType: DASHBOARD_TYPES.INSTANCE,
        },
        data: { filters },
        apolloProvider: createApolloProvider([
          [instanceProjectsQuery, jest.fn().mockResolvedValue(mockVulnerableProjectsInstance())],
        ]),
      });
    });

    it('should render the vulnerabilities', () => {
      expect(findInstanceVulnerabilities().exists()).toBe(true);
    });

    it('shows the vulnerability count list and passes the filters prop', () => {
      expect(findVulnerabilitiesCountList().props('filters')).toBe(filters);
    });

    it('does not show project pipeline status', () => {
      expect(findProjectPipelineStatus().exists()).toBe(false);
    });
  });

  describe('when initialized - group level', () => {
    beforeEach(() => {
      wrapper = createWrapper({
        provide: {
          groupFullPath: 'gitlab-org',
          dashboardType: DASHBOARD_TYPES.GROUP,
        },
        apolloProvider: createApolloProvider([
          [groupProjectsQuery, jest.fn().mockResolvedValue(mockVulnerableProjectsGroup())],
        ]),
      });
    });

    it('should render the vulnerabilities', () => {
      expect(findGroupVulnerabilities().exists()).toBe(true);
    });

    it('displays the vulnerability count list with the correct data', () => {
      expect(findVulnerabilitiesCountList().props()).toEqual({
        filters: wrapper.vm.filters,
      });
    });
  });

  describe('when uninitialized', () => {
    beforeEach(() => {
      wrapper = createWrapper({
        provide: {
          groupFullPath: 'gitlab-org',
          dashboardType: DASHBOARD_TYPES.GROUP,
        },
        apolloProvider: createApolloProvider([
          [
            groupProjectsQuery,
            jest.fn().mockResolvedValue({ data: { group: { projects: { nodes: [] } } } }),
          ],
        ]),
      });
    });

    it('only renders the empty state', () => {
      expect(findAlertsPortalTarget().exists()).toBe(false);
      expect(findGroupEmptyState().exists()).toBe(true);
      expect(findInstanceEmptyState().exists()).toBe(false);
      expect(findProjectEmptyState().exists()).toBe(false);
      expect(findCsvExportButton().exists()).toBe(false);
      expect(findFilters().exists()).toBe(false);
      expect(findVulnerabilitiesCountList().exists()).toBe(false);
      expect(findHeader().exists()).toBe(false);
    });

    it('should show the survey request banner', () => {
      expect(findSurveyRequestBanner().exists()).toBe(true);
    });
  });

  describe('when loading projects', () => {
    beforeEach(() => {
      wrapper = createWrapper({
        provide: { dashboardType: DASHBOARD_TYPES.INSTANCE },
        mocks: { $apollo: { queries: { projects: { loading: true } } } },
      });
    });

    it('renders the loading icon', () => {
      expect(findLoadingIcon().exists()).toBe(true);
    });

    it('does not render the export button, vulnerabilities count list, or header', () => {
      expect(findCsvExportButton().exists()).toBe(false);
      expect(findVulnerabilitiesCountList().exists()).toBe(false);
      expect(findHeader().exists()).toBe(false);
    });

    it('should not show the survey request banner', () => {
      expect(findSurveyRequestBanner().exists()).toBe(false);
    });
  });

  describe('when initialized - project level', () => {
    const createProjectWrapper = ({ securityAutoFix } = {}) =>
      createWrapper({
        provide: {
          dashboardType: DASHBOARD_TYPES.PROJECT,
          autoFixDocumentation: 'path/to/help-page',
          pipeline: { id: '591' },
          glFeatures: { securityAutoFix },
        },
        apolloProvider: createApolloProvider(),
      });

    it('does not show user callout when feature flag is disabled', () => {
      wrapper = createProjectWrapper({ 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');
      wrapper = createProjectWrapper({ securityAutoFix: true });
      const autoFixUserCallOut = findAutoFixUserCallout();
      expect(autoFixUserCallOut.exists()).toBe(true);
      await autoFixUserCallOut.vm.$emit('close');
      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) }));
      wrapper = createProjectWrapper({ securityAutoFix: true });
      expect(findAutoFixUserCallout().exists()).toBe(false);
    });

    it('shows the project pipeline status', () => {
      wrapper = createProjectWrapper();
      expect(findProjectPipelineStatus().exists()).toBe(true);
    });

    it('renders the vulnerabilities', () => {
      wrapper = createProjectWrapper();
      expect(findProjectVulnerabilities().exists()).toBe(true);
    });
  });

  describe('when uninitialized - project level', () => {
    beforeEach(() => {
      wrapper = createWrapper({
        provide: {
          dashboardType: DASHBOARD_TYPES.PROJECT,
        },
        apolloProvider: createApolloProvider(),
      });
    });

    it('renders empty project state', () => {
      expect(findProjectEmptyState().exists()).toBe(true);
    });
  });
});
