import React from 'react';
import {mount} from 'enzyme';

import Indicators from 'app/components/indicators';
import IndicatorStore from 'app/stores/indicatorStore';
import {
  clearIndicators,
  addSuccessMessage,
  addErrorMessage,
  addMessage,
} from 'app/actionCreators/indicator';

// Make sure we use `duration: null` to test add/remove
jest.useFakeTimers();

describe('Indicators', function() {
  let wrapper;
  beforeEach(function() {
    wrapper = mount(<Indicators />, TestStubs.routerContext());

    clearIndicators();
    jest.runAllTimers();
  });

  it('renders nothing by default', function() {
    expect(wrapper.find('ToastIndicator')).toHaveLength(0);
  });

  it('has a loading indicator by default', function() {
    // when "type" is empty, we should treat it as loading state
    IndicatorStore.add('Loading');
    wrapper.update();
    expect(wrapper.find('ToastIndicator')).toHaveLength(1);
  });

  it('adds and removes a toast by calling IndicatorStore directly', function() {
    // when "type" is empty, we should treat it as loading state
    const indicator = IndicatorStore.add('Loading');
    wrapper.update();
    expect(wrapper.find('ToastIndicator')).toHaveLength(1);
    expect(wrapper.find('Message').text()).toBe('Loading');

    jest.runAllTimers();
    wrapper.update();
    expect(wrapper.find('ToastIndicator')).toHaveLength(1);

    // Old indicator gets replaced when a new one is added
    IndicatorStore.remove(indicator);
    jest.runAllTimers();
    wrapper.update();
    expect(wrapper.find('ToastIndicator')).toHaveLength(0);
  });

  // This is a common pattern used throughout the code for API calls
  it('adds and replaces toast by calling IndicatorStore directly', function() {
    IndicatorStore.add('Loading');
    wrapper.update();
    expect(wrapper.find('ToastIndicator')).toHaveLength(1);
    expect(wrapper.find('Message').text()).toBe('Loading');

    // Old indicator gets replaced when a new one is added
    IndicatorStore.add('success', 'success');
    jest.runAllTimers();
    wrapper.update();
    expect(wrapper.find('ToastIndicator')).toHaveLength(1);
    expect(wrapper.find('Message').text()).toBe('success');
  });

  it('does not have loading indicator when "type" is empty (default)', function() {
    addMessage('Loading', '', {duration: null});
    jest.runAllTimers();
    wrapper.update();
    expect(wrapper.find('LoadingIndicator')).toHaveLength(0);
  });

  it('has a loading indicator when type is "loading"', function() {
    addMessage('Loading', 'loading', {duration: null});
    jest.runAllTimers();
    wrapper.update();
    expect(wrapper.find('LoadingIndicator')).toHaveLength(1);
  });

  it('adds and removes toast by calling action creators', function() {
    // action creators don't return anything
    addMessage('Loading', '', {duration: null});
    jest.runAllTimers();
    wrapper.update();
    expect(wrapper.find('ToastIndicator')).toHaveLength(1);
    expect(wrapper.find('Message').text()).toBe('Loading');

    // If no indicator is specified, will remove all indicators
    clearIndicators();
    jest.runAllTimers();
    wrapper.update();
    expect(wrapper.find('ToastIndicator')).toHaveLength(0);
  });

  it('adds and replaces toast by calling action creators', function() {
    addMessage('Loading', '', {duration: null});
    jest.runAllTimers();
    wrapper.update();
    expect(wrapper.find('ToastIndicator')).toHaveLength(1);
    expect(wrapper.find('Message').text()).toBe('Loading');

    // Old indicator gets replaced when a new one is added
    addMessage('success', 'success', {duration: null});
    jest.runAllTimers();
    wrapper.update();
    expect(wrapper.find('ToastIndicator')).toHaveLength(1);
    expect(wrapper.find('Message').text()).toBe('success');
  });

  it('adds and replaces toasts by calling action creators helpers', function() {
    // Old indicator gets replaced when a new one is added
    addSuccessMessage('success', null);
    jest.runAllTimers();
    wrapper.update();
    expect(wrapper.find('ToastIndicator')).toHaveLength(1);
    expect(wrapper.find('Message').text()).toBe('success');

    clearIndicators();
    addErrorMessage('error', null);
    jest.runAllTimers();
    wrapper.update();
    expect(wrapper.find('ToastIndicator')).toHaveLength(1);
    expect(wrapper.find('Message').text()).toBe('error');
  });

  it('appends toasts', function() {
    addMessage('Loading', '', {append: true, duration: null});
    jest.runAllTimers();
    wrapper.update();
    expect(wrapper.find('ToastIndicator')).toHaveLength(1);
    expect(wrapper.find('Message').text()).toBe('Loading');

    addMessage('Success', 'success', {append: true, duration: null});
    jest.runAllTimers();
    wrapper.update();
    expect(wrapper.find('ToastIndicator')).toHaveLength(2);
    // Toasts get appended to the end
    expect(
      wrapper
        .find('Message')
        .at(1)
        .text()
    ).toBe('Success');

    addMessage('Error', 'error', {append: true, duration: null});
    jest.runAllTimers();
    wrapper.update();
    expect(wrapper.find('ToastIndicator')).toHaveLength(3);
    // Toasts get appended to the end
    expect(
      wrapper
        .find('Message')
        .at(2)
        .text()
    ).toBe('Error');

    // clears all toasts
    clearIndicators();
    jest.runAllTimers();
    wrapper.update();
    expect(wrapper.find('ToastIndicator')).toHaveLength(0);
  });

  it('dismisses on click', function() {
    addMessage('Loading', '', {append: true, duration: null});
    jest.runAllTimers();
    wrapper.update();
    expect(wrapper.find('ToastIndicator')).toHaveLength(1);
    expect(wrapper.find('Message').text()).toBe('Loading');

    wrapper.find('ToastIndicator').simulate('click');
    jest.runAllTimers();
    wrapper.update();
    expect(wrapper.find('ToastIndicator')).toHaveLength(0);
  });

  it('hides after 10s', function() {
    addMessage('Duration', '', {append: true, duration: 10000});
    jest.advanceTimersByTime(9000);
    wrapper.update();
    expect(wrapper.find('Indicators')).toHaveLength(1);
    expect(wrapper.find('Indicators').prop('items')).toHaveLength(1);
    expect(wrapper.find('Message').text()).toBe('Duration');

    // Still visible
    jest.advanceTimersByTime(999);
    wrapper.update();
    expect(wrapper.find('Indicators').prop('items')).toHaveLength(1);

    // ToastIndicator still exist because of animations
    // but `items` prop should be empty
    jest.advanceTimersByTime(2);
    wrapper.update();
    expect(wrapper.find('Indicators').prop('items')).toHaveLength(0);

    // After animation timeout
    jest.advanceTimersByTime(1000);
    wrapper.update();
    expect(wrapper.find('ToastIndicator')).toHaveLength(0);
  });
});