import Vue from 'vue';
import { GlDropdown, GlDropdownItem, GlSearchBoxByClick } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
import EnvironmentLogs from 'ee/logs/components/environment_logs.vue';

import { createStore } from 'ee/logs/stores';
import { scrollDown } from '~/lib/utils/scroll_utils';
import {
  mockEnvName,
  mockEnvironments,
  mockPods,
  mockLogsResult,
  mockTrace,
  mockPodName,
  mockSearch,
  mockEnvironmentsEndpoint,
  mockDocumentationPath,
} from '../mock_data';

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

describe('EnvironmentLogs', () => {
  let EnvironmentLogsComponent;
  let store;
  let wrapper;
  let state;

  const propsData = {
    environmentName: mockEnvName,
    environmentsPath: mockEnvironmentsEndpoint,
    clusterApplicationsDocumentationPath: mockDocumentationPath,
  };

  const actionMocks = {
    setInitData: jest.fn(),
    setSearch: jest.fn(),
    showPodLogs: jest.fn(),
    showEnvironment: jest.fn(),
    fetchEnvironments: jest.fn(),
  };

  const updateControlBtnsMock = jest.fn();

  const findEnvironmentsDropdown = () => wrapper.find('.js-environments-dropdown');
  const findPodsDropdown = () => wrapper.find('.js-pods-dropdown');
  const findSearchBar = () => wrapper.find('.js-logs-search');
  const findTimeRangePicker = () => wrapper.find({ ref: 'dateTimePicker' });
  const findInfoAlert = () => wrapper.find('.js-elasticsearch-alert');

  const findLogControlButtons = () => wrapper.find({ name: 'log-control-buttons-stub' });
  const findLogTrace = () => wrapper.find('.js-log-trace');

  const mockSetInitData = () => {
    state.pods.options = mockPods;
    state.environments.current = mockEnvName;
    [state.pods.current] = state.pods.options;

    state.logs.isComplete = false;
    state.logs.lines = mockLogsResult;
  };

  const mockShowPodLogs = podName => {
    state.pods.options = mockPods;
    [state.pods.current] = podName;

    state.logs.isComplete = false;
    state.logs.lines = mockLogsResult;
  };

  const mockFetchEnvs = () => {
    state.environments.options = mockEnvironments;
  };

  const initWrapper = () => {
    wrapper = shallowMount(EnvironmentLogsComponent, {
      propsData,
      store,
      stubs: {
        LogControlButtons: {
          name: 'log-control-buttons-stub',
          template: '<div/>',
          methods: {
            update: updateControlBtnsMock,
          },
        },
      },
      methods: {
        ...actionMocks,
      },
    });
  };

  beforeEach(() => {
    store = createStore();
    state = store.state.environmentLogs;
    EnvironmentLogsComponent = Vue.extend(EnvironmentLogs);
  });

  afterEach(() => {
    actionMocks.setInitData.mockReset();
    actionMocks.showPodLogs.mockReset();
    actionMocks.fetchEnvironments.mockReset();

    if (wrapper) {
      wrapper.destroy();
    }
  });

  it('displays UI elements', () => {
    initWrapper();

    expect(wrapper.isVueInstance()).toBe(true);
    expect(wrapper.isEmpty()).toBe(false);

    // top bar
    expect(findEnvironmentsDropdown().is(GlDropdown)).toBe(true);
    expect(findPodsDropdown().is(GlDropdown)).toBe(true);
    expect(findLogControlButtons().exists()).toBe(true);

    expect(findSearchBar().exists()).toBe(true);
    expect(findSearchBar().is(GlSearchBoxByClick)).toBe(true);
    expect(findTimeRangePicker().exists()).toBe(true);
    expect(findTimeRangePicker().is(DateTimePicker)).toBe(true);

    // log trace
    expect(findLogTrace().isEmpty()).toBe(false);
  });

  it('mounted inits data', () => {
    initWrapper();

    expect(actionMocks.setInitData).toHaveBeenCalledTimes(1);
    expect(actionMocks.setInitData).toHaveBeenLastCalledWith({
      timeRange: expect.objectContaining({
        default: true,
      }),
      environmentName: mockEnvName,
      podName: null,
    });

    expect(actionMocks.fetchEnvironments).toHaveBeenCalledTimes(1);
    expect(actionMocks.fetchEnvironments).toHaveBeenLastCalledWith(mockEnvironmentsEndpoint);
  });

  describe('loading state', () => {
    beforeEach(() => {
      state.pods.options = [];

      state.logs = {
        lines: [],
        isLoading: true,
      };

      state.environments = {
        options: [],
        isLoading: true,
      };

      initWrapper();
    });

    it('displays a disabled environments dropdown', () => {
      expect(findEnvironmentsDropdown().attributes('disabled')).toBe('true');
      expect(findEnvironmentsDropdown().findAll(GlDropdownItem).length).toBe(0);
    });

    it('displays a disabled pods dropdown', () => {
      expect(findPodsDropdown().attributes('disabled')).toBe('true');
      expect(findPodsDropdown().findAll(GlDropdownItem).length).toBe(0);
    });

    it('displays a disabled search bar', () => {
      expect(findSearchBar().exists()).toBe(true);
      expect(findSearchBar().attributes('disabled')).toBe('true');
    });

    it('displays a disabled time window dropdown', () => {
      expect(findTimeRangePicker().attributes('disabled')).toBe('true');
    });

    it('does not update buttons state', () => {
      expect(updateControlBtnsMock).not.toHaveBeenCalled();
    });

    it('shows a logs trace', () => {
      expect(findLogTrace().text()).toBe('');
      expect(
        findLogTrace()
          .find('.js-build-loader-animation')
          .isVisible(),
      ).toBe(true);
    });
  });

  describe('legacy environment', () => {
    beforeEach(() => {
      state.pods.options = [];

      state.logs = {
        lines: [],
        isLoading: false,
      };

      state.environments = {
        options: mockEnvironments,
        current: 'staging',
        isLoading: false,
      };

      initWrapper();
    });

    it('displays a disabled time window dropdown', () => {
      expect(findTimeRangePicker().attributes('disabled')).toBe('true');
    });

    it('displays a disabled search bar', () => {
      expect(findSearchBar().attributes('disabled')).toBe('true');
    });

    it('displays an alert to upgrade to ES', () => {
      expect(findInfoAlert().exists()).toBe(true);
    });
  });

  describe('state with data', () => {
    beforeEach(() => {
      actionMocks.setInitData.mockImplementation(mockSetInitData);
      actionMocks.showPodLogs.mockImplementation(mockShowPodLogs);
      actionMocks.fetchEnvironments.mockImplementation(mockFetchEnvs);

      initWrapper();
    });

    afterEach(() => {
      scrollDown.mockReset();
      updateControlBtnsMock.mockReset();

      actionMocks.setInitData.mockReset();
      actionMocks.showPodLogs.mockReset();
      actionMocks.fetchEnvironments.mockReset();
    });

    it('displays an enabled search bar', () => {
      expect(findSearchBar().attributes('disabled')).toBeFalsy();

      // input a query and click `search`
      findSearchBar().vm.$emit('input', mockSearch);
      findSearchBar().vm.$emit('submit');

      expect(actionMocks.setSearch).toHaveBeenCalledTimes(1);
      expect(actionMocks.setSearch).toHaveBeenCalledWith(mockSearch);
    });

    it('displays an enabled time window dropdown', () => {
      expect(findTimeRangePicker().attributes('disabled')).toBeFalsy();
    });

    it('does not display an alert to upgrade to ES', () => {
      expect(findInfoAlert().exists()).toBe(false);
    });

    it('populates environments dropdown', () => {
      const items = findEnvironmentsDropdown().findAll(GlDropdownItem);
      expect(findEnvironmentsDropdown().props('text')).toBe(mockEnvName);
      expect(items.length).toBe(mockEnvironments.length);
      mockEnvironments.forEach((env, i) => {
        const item = items.at(i);
        expect(item.text()).toBe(env.name);
      });
    });

    it('populates pods dropdown', () => {
      const items = findPodsDropdown().findAll(GlDropdownItem);

      expect(findPodsDropdown().props('text')).toBe(mockPodName);
      expect(items.length).toBe(mockPods.length);
      mockPods.forEach((pod, i) => {
        const item = items.at(i);
        expect(item.text()).toBe(pod);
      });
    });

    it('populates logs trace', () => {
      const trace = findLogTrace();
      expect(trace.text().split('\n').length).toBe(mockTrace.length);
      expect(trace.text().split('\n')).toEqual(mockTrace);
    });

    it('update control buttons state', () => {
      expect(updateControlBtnsMock).toHaveBeenCalledTimes(1);
    });

    it('scrolls to bottom when loaded', () => {
      expect(scrollDown).toHaveBeenCalledTimes(1);
    });

    describe('when user clicks', () => {
      it('environment name, trace is refreshed', () => {
        const items = findEnvironmentsDropdown().findAll(GlDropdownItem);
        const index = 1; // any env

        expect(actionMocks.showEnvironment).toHaveBeenCalledTimes(0);

        items.at(index).vm.$emit('click');

        expect(actionMocks.showEnvironment).toHaveBeenCalledTimes(1);
        expect(actionMocks.showEnvironment).toHaveBeenLastCalledWith(mockEnvironments[index].name);
      });

      it('pod name, trace is refreshed', () => {
        const items = findPodsDropdown().findAll(GlDropdownItem);
        const index = 2; // any pod

        expect(actionMocks.showPodLogs).toHaveBeenCalledTimes(0);

        items.at(index).vm.$emit('click');

        expect(actionMocks.showPodLogs).toHaveBeenCalledTimes(1);
        expect(actionMocks.showPodLogs).toHaveBeenLastCalledWith(mockPods[index]);
      });

      it('refresh button, trace is refreshed', () => {
        expect(actionMocks.showPodLogs).toHaveBeenCalledTimes(0);

        findLogControlButtons().vm.$emit('refresh');

        expect(actionMocks.showPodLogs).toHaveBeenCalledTimes(1);
        expect(actionMocks.showPodLogs).toHaveBeenLastCalledWith(mockPodName);
      });
    });
  });
});
