/* eslint-disable camelcase */
import React from 'react';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import { MemoryRouter } from 'react-router-dom';
import promiseMiddleware from 'redux-promise-middleware';
import { IntlProvider } from 'react-intl';
import Requests from '../../../smart-components/request/requests';
import requestReducer, { requestsInitialState } from '../../../redux/reducers/request-reducer';
import { act } from 'react-dom/test-utils';
import ReducerRegistry, { applyReducerHash } from '@redhat-cloud-services/frontend-components-utilities/ReducerRegistry';
import { APPROVAL_API_BASE } from '../../../utilities/constants';
import TableEmptyState from '../../../presentational-components/shared/table-empty-state';
import UserContext from '../../../user-context';
import routes from '../../../constants/routes';
import ActionModal from '../../../smart-components/request/action-modal';
import { APPROVAL_APPROVER_ROLE } from '../../../helpers/shared/helpers';

const roles = {};
roles[APPROVAL_APPROVER_ROLE] = true;

const ComponentWrapper = ({ store, initialEntries = [ '/requests' ], children }) => (
  <IntlProvider locale="en">
    <UserContext.Provider value={ { userRoles: roles } } >
      <Provider store={ store } >
        <MemoryRouter initialEntries={ initialEntries }>
          <IntlProvider locale="en">
            { children }
          </IntlProvider>
        </MemoryRouter>
      </Provider>
    </UserContext.Provider>
  </IntlProvider>
);

describe('<Requests />', () => {
  let initialProps;

  const request = {
    id: '299',
    state: 'notified',
    decision: 'undecided',
    workflow_id: '1',
    created_at: '2020-01-08T19:37:59Z',
    notified_at: '2020-01-08T19:37:59Z',
    number_of_children: 0,
    number_of_finished_children: 0,
    owner: 'jsmith@redhat.com',
    requester_name: 'John Smith',
    name: 'QA Password survey field'
  };

  beforeEach(() => {
    initialProps = {};
  });

  it('should sort requests when click on sort', async () => {
    expect.assertions(3);

    apiClientMock.get(`${APPROVAL_API_BASE}/requests/?limit=50&offset=0&sort_by=created_at%3Adesc`, mockOnce({
      status: 200,
      body: {
        meta: { count: 1, limit: 50, offset: 0 },
        data: [ request ]
      }
    }));

    const registry = new ReducerRegistry({}, [ thunk, promiseMiddleware ]);
    registry.register({ requestReducer: applyReducerHash(requestReducer, requestsInitialState) });
    const store = registry.getStore();

    let wrapper;
    await act(async () => {
      wrapper = mount(<ComponentWrapper store={ store }><Requests { ...initialProps } /></ComponentWrapper>);
    });
    wrapper.update();

    apiClientMock.get(`${APPROVAL_API_BASE}/requests/?limit=50&offset=0&sort_by=name%3Aasc`,
      mockOnce((req, res) => {
        expect(req.url().query).toEqual({
          limit: '50', offset: '0', sort_by: 'name:asc'
        });
        return res.status(200).body({
          meta: { count: 1, limit: 50, offset: 0 },
          data: [ request ]
        });
      })
    );

    await act(async () => {
      wrapper.find('button').at(5).simulate('click'); // name column
    });
    wrapper.update();

    apiClientMock.get(
      `${APPROVAL_API_BASE}/requests/?limit=50&offset=0&sort_by=name%3Adesc`,
      mockOnce((req, res) => {
        expect(req.url().query).toEqual({
          limit: '50', offset: '0', sort_by: 'name:desc'
        });
        return res.status(200).body({
          meta: { count: 1, limit: 50, offset: 0 },
          data: [ request ]
        });
      })
    );

    await act(async () => {
      wrapper.find('button').at(5).simulate('click'); // name column
    });
    wrapper.update();

    apiClientMock.get(
      `${APPROVAL_API_BASE}/requests/?limit=50&offset=0&sort_by=requester_name%3Aasc`,
      mockOnce((req, res) => {
        expect(req.url().query).toEqual({
          limit: '50', offset: '0', sort_by: 'requester_name:asc'
        });
        return res.status(200).body({
          meta: { count: 1, limit: 50, offset: 0 },
          data: [ request ]
        });
      })
    );

    await act(async () => {
      wrapper.find('button').at(6).simulate('click'); // requester column
    });
    wrapper.update();
  });

  it('should filter requests - and clear filters', async () => {
    jest.useFakeTimers();
    expect.assertions(6);

    apiClientMock.get(`${APPROVAL_API_BASE}/requests/?limit=50&offset=0&sort_by=created_at%3Adesc`, mockOnce({
      status: 200,
      body: {
        meta: { count: 1, limit: 50, offset: 0 },
        data: [ request ]
      }
    }));

    const registry = new ReducerRegistry({}, [ thunk, promiseMiddleware ]);
    registry.register({ requestReducer: applyReducerHash(requestReducer, requestsInitialState) });
    const store = registry.getStore();

    let wrapper;
    await act(async () => {
      wrapper = mount(<ComponentWrapper store={ store }><Requests { ...initialProps } /></ComponentWrapper>);
    });
    wrapper.update();

    apiClientMock.get(`${APPROVAL_API_BASE}/requests/?filter%5Bname%5D%5Bcontains_i%5D=some-name&limit=50&offset=0&sort_by=created_at%3Adesc`,
      mockOnce((req, res) => {
        expect(req.url().query).toEqual({
          'filter[name][contains_i]': 'some-name', limit: '50', offset: '0', sort_by: 'created_at:desc'
        });
        return res.status(200).body({
          meta: { count: 1, limit: 50, offset: 0 },
          data: [ request ]
        });
      })
    );

    await act(async () => {
      wrapper.find('input').first().instance().value = 'some-name';
      wrapper.find('input').first().simulate('change');
    });
    wrapper.update();
    await act(async () => {
      jest.runAllTimers();
    });
    wrapper.update();

    // Switch to requester name filter
    await act(async () => {
      wrapper.find('ConditionalFilter').setState({ stateValue: 1 });
    });
    wrapper.update();

    apiClientMock.get(
      `${APPROVAL_API_BASE}/requests/?filter%5Bname%5D%5Bcontains_i%5D=some-name&filter%5Brequester_name%5D%5Bcontains_i%5D=some-requester`
       + `&limit=50&offset=0&sort_by=created_at%3Adesc`,
      mockOnce((req, res) => {
        expect(req.url().query).toEqual({
          'filter[name][contains_i]': 'some-name',
          'filter[requester_name][contains_i]': 'some-requester',
          limit: '50',
          offset: '0',
          sort_by: 'created_at:desc'
        });
        return res.status(200).body({
          meta: { count: 1, limit: 50, offset: 0 },
          data: [ request ]
        });
      })
    );

    await act(async () => {
      wrapper.find('input').first().instance().value = 'some-requester';
      wrapper.find('input').first().simulate('change');
    });
    wrapper.update();
    await act(async () => {
      jest.runAllTimers();
    });
    wrapper.update();

    // Switch to status filter
    await act(async () => {
      wrapper.find('ConditionalFilter').setState({ stateValue: 2 });
    });
    wrapper.update();

    apiClientMock.get(
      `${APPROVAL_API_BASE}/requests/?filter%5Bname%5D%5Bcontains_i%5D=some-name&filter%5Brequester_name%5D%5Bcontains_i%5D=some-requester`
       + '&filter%5Bdecision%5D%5Beq%5D%5B%5D=canceled&filter%5Bdecision%5D%5Beq%5D%5B%5D=approved'
      + '&limit=50&offset=0&sort_by=created_at%3Adesc',
      mockOnce((req, res) => {
        expect(req.url().query).toEqual({
          'filter[name][contains_i]': 'some-name',
          'filter[requester_name][contains_i]': 'some-requester',
          'filter[decision][eq][]': [ 'canceled', 'approved' ],
          limit: '50',
          offset: '0',
          sort_by: 'created_at:desc'
        });
        return res.status(200).body({
          meta: { count: 1, limit: 50, offset: 0 },
          data: [ request ]
        });
      })
    );

    const checkboxDropdownProps = wrapper.find('Select').last().props();

    const EVENT = {};
    await act(async () => {
      checkboxDropdownProps.onSelect(EVENT, 'canceled');
    });
    wrapper.update();
    await act(async () => {
      checkboxDropdownProps.onSelect(EVENT, 'approved');
    });
    wrapper.update();

    await act(async () => {
      jest.runAllTimers();
    });
    wrapper.update();

    apiClientMock.get(
      `${APPROVAL_API_BASE}/requests/?filter%5Bname%5D%5Bcontains_i%5D=some-name&filter%5Brequester_name%5D%5Bcontains_i%5D=some-requester`
       + '&filter%5Bdecision%5D%5Beq%5D%5B%5D=approved'
      + '&limit=50&offset=0&sort_by=created_at%3Adesc',
      mockOnce((req, res) => {
        expect(req.url().query).toEqual({
          'filter[name][contains_i]': 'some-name',
          'filter[requester_name][contains_i]': 'some-requester',
          'filter[decision][eq][]': 'approved',
          limit: '50',
          offset: '0',
          sort_by: 'created_at:desc'
        });
        return res.status(200).body({
          meta: { count: 1, limit: 50, offset: 0 },
          data: [ request ]
        });
      })
    );

    await act(async () => {
      checkboxDropdownProps.onSelect(EVENT, 'canceled');
    });
    wrapper.update();

    await act(async () => {
      jest.runAllTimers();
    });
    wrapper.update();

    apiClientMock.get(
      `${APPROVAL_API_BASE}/requests/?filter%5Bname%5D%5Bcontains_i%5D=some-name&filter%5Brequester_name%5D%5Bcontains_i%5D=some-requester`
      + '&limit=50&offset=0&sort_by=created_at%3Adesc',
      mockOnce((req, res) => {
        expect(req.url().query).toEqual({
          'filter[name][contains_i]': 'some-name',
          'filter[requester_name][contains_i]': 'some-requester',
          limit: '50',
          offset: '0',
          sort_by: 'created_at:desc'
        });
        return res.status(200).body({
          meta: { count: 1, limit: 50, offset: 0 },
          data: [ request ]
        });
      })
    );

    // clear one chip
    await act(async () => {
      wrapper.find('.pf-c-chip').last().find('button').simulate('click');
    });
    wrapper.update();
    await act(async () => {
      jest.runAllTimers();
    });
    wrapper.update();

    apiClientMock.get(
      `${APPROVAL_API_BASE}/requests/?limit=50&offset=0&sort_by=created_at%3Adesc`,
      mockOnce((req, res) => {
        expect(req.url().query).toEqual({
          limit: '50',
          offset: '0',
          sort_by: 'created_at:desc'
        });
        return res.status(200).body({
          meta: { count: 1, limit: 50, offset: 0 },
          data: [ request ]
        });
      })
    );

    // clear chips
    await act(async () => {
      wrapper.find('.ins-c-chip-filters').find('button').last().simulate('click');
    });
    wrapper.update();

    await act(async () => {
      jest.runAllTimers();
    });
    wrapper.update();

    jest.useRealTimers();
  });

  it('should paginate requests', async () => {
    jest.useFakeTimers();
    expect.assertions(1);

    apiClientMock.get(`${APPROVAL_API_BASE}/requests/?limit=50&offset=0&sort_by=created_at%3Adesc`, mockOnce({
      status: 200,
      body: {
        meta: { count: 1, limit: 50, offset: 0 },
        data: [ request ]
      }
    }));

    apiClientMock.get(`${APPROVAL_API_BASE}/requests/?limit=10&offset=2&sort_by=created_at%3Adesc`, mockOnce({
      status: 200,
      body: {
        meta: { count: 1, limit: 50, offset: 0 },
        data: [ request ]
      }
    }));

    const registry = new ReducerRegistry({}, [ thunk, promiseMiddleware ]);
    registry.register({ requestReducer: applyReducerHash(requestReducer, requestsInitialState) });
    const store = registry.getStore();

    let wrapper;
    await act(async () => {
      wrapper = mount(<ComponentWrapper store={ store }><Requests { ...initialProps } /></ComponentWrapper>);
    });
    wrapper.update();

    apiClientMock.get(`${APPROVAL_API_BASE}/requests/?limit=10&offset=0&sort_by=created_at%3Adesc`,
      mockOnce((req, res) => {
        expect(req.url().query).toEqual({
          limit: '10', offset: '0', sort_by: 'created_at:desc'
        });
        return res.status(200).body({
          meta: { count: 30, limit: 10, offset: 0 },
          data: [ request ]
        });
      })
    );

    await act(async () => {
      wrapper.find('.pf-c-options-menu__toggle-button').first().simulate('click');
    });
    wrapper.update();
    await act(async () => {
      wrapper.find('.pf-c-options-menu__menu-item').first().simulate('click');
    });
    wrapper.update();
    await act(async () => {
      jest.runAllTimers();
    });
    wrapper.update();

    apiClientMock.get(`${APPROVAL_API_BASE}/requests/?limit=10&offset=10&sort_by=created_at%3Adesc`,
      mockOnce((req, res) => {
        expect(req.url().query).toEqual({
          limit: '10', offset: '10', sort_by: 'created_at:desc'
        });
        return res.status(200).body({
          meta: { count: 30, limit: 10, offset: 0 },
          data: [ request ]
        });
      })
    );

    await act(async () => {
      wrapper.find('.pf-c-pagination__nav').first().find('button').last().simulate('click');
    });
    wrapper.update();
    await act(async () => {
      jest.runAllTimers();
    });
    wrapper.update();

    jest.useRealTimers();
  });

  it('should render table empty state', async () => {
    apiClientMock.get(`${APPROVAL_API_BASE}/requests/?limit=50&offset=0&sort_by=created_at%3Adesc`, mockOnce({
      status: 200,
      body: {
        meta: { count: 0, limit: 50, offset: 0 },
        data: [ ]
      }
    }));

    const registry = new ReducerRegistry({}, [ thunk, promiseMiddleware ]);
    registry.register({ requestReducer: applyReducerHash(requestReducer, requestsInitialState) });
    const store = registry.getStore();

    let wrapper;
    await act(async () => {
      wrapper = mount(<ComponentWrapper store={ store }><Requests { ...initialProps } /></ComponentWrapper>);
    });
    wrapper.update();

    expect(wrapper.find(TableEmptyState)).toHaveLength(1);
  });

  describe('Actions', () => {
    const request = {
      id: '703',
      state: 'notified',
      decision: 'undecided',
      workflow_id: '168',
      created_at: '2020-05-13T13:35:48Z',
      notified_at: '2020-05-13T13:36:29Z',
      number_of_children: 0,
      number_of_finished_children: 0,
      owner: 'insights-qa',
      requester_name: 'Insights QA',
      name: 'Hello World',
      group_name: 'Test Approval Group',
      parent_id: '702',
      metadata: {
        user_capabilities: { show: true, create: true, approve: true, cancel: false, deny: true, memo: true }
      }
    };

    beforeEach(() => {
      apiClientMock.get(`${APPROVAL_API_BASE}/requests/?limit=50&offset=0&sort_by=created_at%3Adesc`, mockOnce({
        status: 200,
        body: {
          meta: { count: 1, limit: 50, offset: 0 },
          data: [ request ]
        }
      }));
    });

    it('should open comment modal', async () => {
      const registry = new ReducerRegistry({}, [ thunk, promiseMiddleware ]);
      registry.register({ requestReducer: applyReducerHash(requestReducer, requestsInitialState) });
      const store = registry.getStore();

      let wrapper;
      await act(async () => {
        wrapper = mount(
          <ComponentWrapper store={ store }><Requests { ...initialProps } /></ComponentWrapper>
        );
      });
      wrapper.update();

      expect(wrapper.find(ActionModal)).toHaveLength(0);
      expect(wrapper.find(MemoryRouter).instance().history.location.pathname).toEqual(routes.requests.index);

      await act(async () => {
        wrapper.find(`a#comment-${request.id}`).simulate('click', { button: 0 });
      });
      wrapper.update();

      expect(wrapper.find(ActionModal).props().actionType).toEqual('Comment');
      expect(wrapper.find(ActionModal).props().postMethod).toBeDefined();
      expect(wrapper.find(MemoryRouter).instance().history.location.pathname).toEqual(routes.requests.comment);
      expect(wrapper.find(MemoryRouter).instance().history.location.search).toEqual('?request=703');
    });

    it('should open approve modal from the list', async () => {
      const registry = new ReducerRegistry({}, [ thunk, promiseMiddleware ]);
      registry.register({ requestReducer: applyReducerHash(requestReducer, requestsInitialState) });
      const store = registry.getStore();

      let wrapper;
      await act(async () => {
        wrapper = mount(
          <ComponentWrapper store={ store }><Requests { ...initialProps } /></ComponentWrapper>
        );
      });
      wrapper.update();

      expect(wrapper.find(ActionModal)).toHaveLength(0);
      expect(wrapper.find(MemoryRouter).instance().history.location.pathname).toEqual(routes.requests.index);

      await act(async () => {
        wrapper.find(`a#approve-${request.id}`).simulate('click', { button: 0 });
      });
      wrapper.update();

      expect(wrapper.find(ActionModal).props().actionType).toEqual('Approve');
      expect(wrapper.find(ActionModal).props().postMethod).toBeDefined();
      expect(wrapper.find(MemoryRouter).instance().history.location.pathname).toEqual(routes.requests.approve);
      expect(wrapper.find(MemoryRouter).instance().history.location.search).toEqual('?request=703');
    });

    it('should open deny modal from the list', async () => {
      const registry = new ReducerRegistry({}, [ thunk, promiseMiddleware ]);
      registry.register({ requestReducer: applyReducerHash(requestReducer, requestsInitialState) });
      const store = registry.getStore();

      let wrapper;
      await act(async () => {
        wrapper = mount(
          <ComponentWrapper store={ store }><Requests { ...initialProps } /></ComponentWrapper>
        );
      });
      wrapper.update();

      expect(wrapper.find(ActionModal)).toHaveLength(0);
      expect(wrapper.find(MemoryRouter).instance().history.location.pathname).toEqual(routes.requests.index);

      await act(async () => {
        wrapper.find(`a#deny-${request.id}`).simulate('click', { button: 0 });
      });
      wrapper.update();

      expect(wrapper.find(ActionModal).props().actionType).toEqual('Deny');
      expect(wrapper.find(ActionModal).props().postMethod).toBeDefined();
      expect(wrapper.find(MemoryRouter).instance().history.location.pathname).toEqual(routes.requests.deny);
      expect(wrapper.find(MemoryRouter).instance().history.location.search).toEqual('?request=703');
    });
  });
});
