import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import JestMockPromise from 'jest-mock-promise';
import React from 'react';
import { applyMiddleware, createStore } from 'redux';
import { persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import promise from 'redux-promise-middleware';
import thunk from 'redux-thunk';
import { LOGGED_IN } from '../../constants/actionTypes';
import { getContent } from '../../helpers/eventHelpers';
import reducer from '../../reducers';
import { createEventsWithEmptyContent } from '../helpers';
import getUIConfiguration from '../uiConfig';

const resources = require('./resources/LogbookContainer.resource.js');

require('it-each')({ testPerIteration: true });

beforeEach(() => {
  Enzyme.configure({ adapter: new Adapter() });
  jest.resetModules();
  jest.useFakeTimers();
});

describe.skip('LogbookContainerIntegrationTests', () => {
  describe('rendering', () => {
    it.each(
      resources.rendering.panels,
      '%s',
      ['description'],
      (element, next) => {
        const mockGetUIConfiguration = getUIConfiguration({
          AUTOREFRESH_EVENTLIST: false,
        });
        jest.mock('../../config/ui/config', () => mockGetUIConfiguration);

        // require logbook class after the configuration mocking
        const LogbookContainerClass = require('../../containers/Logbook/LogbookContainer')
          .LogbookContainer;
        const getEventsBySelectionFilterPromise = new JestMockPromise(
          (resolve) => resolve(element.getEventsBySelectionFilterResponse1)
        );
        const getEventCountBySelectionFilterPromise = new JestMockPromise(
          (resolve) => resolve(element.getEventCountBySelectionFilterResponse1)
        );

        LogbookContainerClass.prototype.getEventsBySelectionFilter = () =>
          getEventsBySelectionFilterPromise;
        LogbookContainerClass.prototype.getEventCountBySelectionFilter = () =>
          getEventCountBySelectionFilterPromise;

        const wrapper = getMountedWrapper({
          investigationId: element.investigationId,
          releasedInvestigations: element.releasedInvestigations,
        });
        getEventsBySelectionFilterPromise.resolve();
        getEventCountBySelectionFilterPromise.resolve();

        wrapper.update();

        // It renders the animation widget first
        expect(wrapper.find('Loading')).toBeDefined();

        wrapper.find('EventList').find('Button').simulate('click');
        jest.advanceTimersByTime(100); // advance the timer included in the react-responsive-modal despite its animationDuration prop set to 0
        wrapper.update();
        expect(wrapper.find('OverlayBox').at(1).exists(element.expected)).toBe(
          true
        );
        next();
      }
    );
  });

  describe('flipEventsSortingOrder', () => {
    it.each(
      resources.flipEventsSortingOrder,
      '%s',
      ['description'],
      (element, next) => {
        //mock the configuration file. Since mockGetUIConfiguration is out of scope of jest.mock(), it must start with 'mock'.
        const mockGetUIConfiguration = getUIConfiguration({
          SORT_EVENTS_BY: element.SORT_EVENTS_BY,
          SORTING_ORDER: element.SORTING_ORDER,
          AUTOREFRESH_EVENTLIST: false,
        });
        jest.mock('../../config/ui/config', () => mockGetUIConfiguration);

        // require logbook class after the configuration mocking
        const LogbookContainerClass = require('../../containers/Logbook/LogbookContainer')
          .LogbookContainer;
        LogbookContainerClass.prototype.getEventsBySelectionFilter = jest.fn(
          () => Promise.resolve(element.serverResponse_No_Event)
        );
        LogbookContainerClass.prototype.getEventCountBySelectionFilter = jest.fn(
          () => Promise.resolve(element.serverResponse_No_EventCount)
        );
        expect(
          LogbookContainerClass.prototype.getEventsBySelectionFilter
        ).toHaveBeenCalledTimes(0);

        const mountedWrapper = getMountedWrapper({});
        mountedWrapper.update();
        expect(
          LogbookContainerClass.prototype.getEventsBySelectionFilter
        ).toHaveBeenCalledTimes(1);
        expect(
          LogbookContainerClass.prototype.getEventsBySelectionFilter
        ).toHaveBeenCalledWith(
          expect.anything(),
          expect.anything(),
          element.expected.sortingFilterArgAtfirstCall
        );

        mountedWrapper
          .find('EventListMenu')
          .find({ eventKey: element.eventKey })
          .find('a')
          .simulate('click');

        // check the event list is flipped ie the proper call to the server is done; offset is 0 (ie show first page)
        expect(
          LogbookContainerClass.prototype.getEventsBySelectionFilter
        ).toHaveBeenCalledTimes(2);
        expect(
          LogbookContainerClass.prototype.getEventsBySelectionFilter
        ).toHaveBeenCalledWith(
          0,
          expect.anything(),
          element.expected.sortingFilterArgAtSecondCall
        );

        next();
      }
    );
  });

  describe('autorefresh', () => {
    it.each(
      resources.autorefresh,
      '%s',
      ['description'],
      async (element, next) => {
        // Mock the configuration file. Since mockGetUIConfiguration is out of scope of jest.mock(), it must start with 'mock'.
        const mockGetUIConfiguration = getUIConfiguration({
          SORT_EVENTS_BY: element.SORT_EVENTS_BY,
          SORTING_ORDER: element.SORTING_ORDER,
          AUTOREFRESH_EVENTLIST: true,
          AUTOREFRESH_DELAY: element.AUTOREFRESH_DELAY,
        });
        jest.mock('../../config/ui/config', () => mockGetUIConfiguration);

        // require logbook class after the configuration mocking
        const LogbookContainerClass = require('../../containers/Logbook/LogbookContainer')
          .LogbookContainer;
        // the first requests triggered by bothe the logbook at initialization and then periodic refresher
        let getEventsBySelectionFilterPromise = new JestMockPromise((resolve) =>
          resolve(element.getEventsBySelectionFilterResponse1)
        );

        // Initialize LogbookContainer
        LogbookContainerClass.prototype.getEventsBySelectionFilter = () =>
          getEventsBySelectionFilterPromise;
        LogbookContainerClass.prototype.getEventCountBySelectionFilter = () =>
          Promise.resolve(element.getEventCountBySelectionFilterResponse1);
        const mountedWrapper = getMountedWrapper({});
        getEventsBySelectionFilterPromise.resolve(); // manually trigger 'then' in componentDidMount()

        // Test EventList is rendered
        mountedWrapper.update();
        expect(
          getContent(
            mountedWrapper.find('Event').at(0).prop('event').content,
            'plainText'
          )
        ).toEqual(element.expected.firstEventInListBeforeClick);

        // Mimics a call triggered by the periodic refresher which does not resolve immediately (pending request)
        const getEventsBySelectionFilterPromise2 = new JestMockPromise(
          (resolve) => {
            setTimeout(() => {
              return resolve(element.getEventsBySelectionFilterResponse1);
            }, element.AUTOREFRESH_DELAY + 500);
          }
        );
        mountedWrapper
          .find('LogbookContainer')
          .instance().getEventsBySelectionFilter = () =>
          getEventsBySelectionFilterPromise2;
        await console.log(
          'Triggers periodic refresh timers and only the first timer of the promise'
        );
        //await jest.runOnlyPendingTimers(); // Triggers periodic refresh timers and only the first timer of the promise
        jest.runTimersToTime(element.AUTOREFRESH_DELAY);

        // User clicks to sort oldest event first
        await console.log('user clicks');
        getEventsBySelectionFilterPromise = new JestMockPromise((resolve) =>
          resolve(element.getEventsBySelectionFilterResponse2)
        );
        mountedWrapper
          .find('LogbookContainer')
          .instance().getEventsBySelectionFilter = () =>
          getEventsBySelectionFilterPromise;
        await mountedWrapper
          .find('EventListMenu')
          .find({ eventKey: element.eventKey })
          .find('a')
          .simulate('click');
        await getEventsBySelectionFilterPromise.resolve(); // manually trigger 'then' in flipEventSortingOrder()

        // The event list is rendered. Check that oldest event is before youngest event
        await console.log('check render after clicking');
        await mountedWrapper.update();
        await expect(
          getContent(
            mountedWrapper.find('Event').at(0).prop('event').content,
            'plainText'
          )
        ).toEqual(element.expected.firstEventInListAfterClick);

        // Mimic the pending call from periodic refresher has received its data
        await console.log(
          'Triggers periodic refresh timer and the second timer of the promise'
        );
        jest.runTimersToTime(500);
        await getEventsBySelectionFilterPromise2.resolve(); // manually trigger 'then' in onNewEventCountReceived()

        // Test that the Eventist is unchanged
        await console.log('check render a last');
        await mountedWrapper.update();
        await expect(
          getContent(
            mountedWrapper.find('Event').at(0).prop('event').content,
            'plainText'
          )
        ).toEqual(element.expected.firstEventInListAfterClick);

        next();
      }
    );
  });
});

describe.skip('logbookPages', () => {
  it.each(
    resources.logbookPages,
    '%s',
    ['description'],
    async (element, next) => {
      /**
       * Helper function which clicks the next page until page link is visible in the LogbookPager
       * @param {*} page seached page which is targetted (so needs to be visible)
       * @param {*} wrapper
       * @param {*} getEventsBySelectionFilterPromise
       * @param {*} getEventCountBySelectionFilterPromise
       */
      async function clickNextPage(
        page,
        wrapper,
        getEventsBySelectionFilterPromise,
        getEventCountBySelectionFilterPromise
      ) {
        while (
          !wrapper
            .find('EventListMenu')
            .find('LogbookPager')
            .findWhere((w) => w.prop('page') === page)
            .exists()
        ) {
          getEventsBySelectionFilterPromise = Promise.resolve(
            createEventsWithEmptyContent(element.EVENTS_PER_DOWNLOAD)
          );
          getEventCountBySelectionFilterPromise = Promise.resolve(
            element.getEventCountBySelectionFilterResponse
          );
          wrapper
            .find('EventListMenu')
            .find('LogbookPager')
            .find('.next')
            .find('a')
            .simulate('click');
          await getEventsBySelectionFilterPromise; // dummy events
          await getEventCountBySelectionFilterPromise;
          wrapper.update();
        }
      }

      // Mock the configuration file. Since mockGetUIConfiguration is out of scope of jest.mock(), it must start with 'mock'.
      const mockGetUIConfiguration = getUIConfiguration({
        AUTOREFRESH_EVENTLIST: element.AUTOREFRESH_EVENTLIST,
        AUTOREFRESH_DELAY: element.AUTOREFRESH_DELAY,
        EVENTS_PER_PAGE: element.EVENTS_PER_PAGE,
        EVENTS_PER_DOWNLOAD: element.EVENTS_PER_DOWNLOAD,
        SORT_EVENTS_BY: '_id',
      });
      jest.mock('../../config/ui/config', () => mockGetUIConfiguration);

      // Require logbook class after the configuration mocking
      const LogbookContainerClass = require('../../containers/Logbook/LogbookContainer')
        .LogbookContainer;

      let getEventsBySelectionFilterPromise = Promise.resolve(
        element.getEventsBySelectionFilterResponseAtInitialization
      );
      let getEventCountBySelectionFilterPromise = Promise.resolve(
        element.getEventCountBySelectionFilterResponse
      );

      const mockedGetEventsBySelectionFilter = jest
        .fn()
        .mockImplementation(() => getEventsBySelectionFilterPromise);

      LogbookContainerClass.prototype.getEventsBySelectionFilter = mockedGetEventsBySelectionFilter;
      LogbookContainerClass.prototype.getEventCountBySelectionFilter = () =>
        getEventCountBySelectionFilterPromise;

      // Initialize LogbookContainer. EventList shows page 1.
      const mountedWrapper = getMountedWrapper({});
      await getEventsBySelectionFilterPromise;
      await getEventCountBySelectionFilterPromise;
      mountedWrapper.update();

      // User clicks on the page {element.startingPage}
      for (
        let nextPageClick = 1;
        nextPageClick < element.startingPage;
        nextPageClick++
      ) {
        getEventsBySelectionFilterPromise = new JestMockPromise(() => null);
        getEventCountBySelectionFilterPromise = new JestMockPromise(() => null);
        mountedWrapper
          .find('EventListMenu')
          .find('LogbookPager')
          .find('.next')
          .find('a')
          .simulate('click');
        console.log(`nextPageClick vaut; ${nextPageClick}`);
        if (nextPageClick === element.startingPage - 1) {
          getEventsBySelectionFilterPromise.resolve(
            element.getEventsBySelectionFilterResponseForStartingPage
          );
          getEventCountBySelectionFilterPromise.resolve(
            element.getEventCountBySelectionFilterResponse
          );
        } else {
          getEventsBySelectionFilterPromise.resolve(
            createEventsWithEmptyContent(4)
          ); // dummy events
          getEventCountBySelectionFilterPromise.resolve(
            element.getEventCountBySelectionFilterResponse
          );
        }
        mountedWrapper.update();
      }

      // Checks events before changing the page
      expect(mountedWrapper.find('EventList').prop('events')).toEqual(
        element.expected.eventsOnStartingPage
      );

      // User wants to click on the page {element.clickedPage}. If it is not visible click the '>'. page as many time as needed
      clickNextPage(
        element.clickedPage,
        mountedWrapper,
        getEventsBySelectionFilterPromise,
        getEventCountBySelectionFilterPromise
      );

      // User clicks on the page {element.clickedPage}
      mockedGetEventsBySelectionFilter.mockClear(); // forget all previous calls to getEventsBySelectionFilter
      getEventsBySelectionFilterPromise = Promise.resolve(
        element.getEventsBySelectionFilterResponseForClickedPage
      );
      getEventCountBySelectionFilterPromise = Promise.resolve(
        element.getEventCountBySelectionFilterResponse
      );

      mountedWrapper
        .find('EventListMenu')
        .find('LogbookPager')
        .findWhere((w) => w.prop('page') === element.clickedPage)
        .find('a')
        .simulate('click');

      if (element.getEventsBySelectionFilterResponseForClickedPage) {
        await getEventsBySelectionFilterPromise;
        await getEventCountBySelectionFilterPromise;
        mountedWrapper.update();
      }

      // Check that no additional request is triggered when events are available locally
      expect(mockedGetEventsBySelectionFilter).toHaveBeenCalledTimes(
        element.expected.getEventsBySelectionFilterCallsCount
      );

      // Check events after changing the page
      expect(mountedWrapper.find('EventList').prop('events')).toEqual(
        element.expected.eventsOnClickedPage
      );

      // Check the current page
      expect(
        mountedWrapper
          .find('EventListMenu')
          .find('LogbookPager')
          .findWhere((w) => !!w.prop('selected'))
          .prop('page')
      ).toEqual(element.clickedPage);
      expect(
        mountedWrapper
          .find('EventList')
          .find('LogbookPager')
          .findWhere((w) => !!w.prop('selected'))
          .prop('page')
      ).toEqual(element.clickedPage);

      if (element.AUTOREFRESH_EVENTLIST) {
        mockedGetEventsBySelectionFilter.mockClear();
        getEventsBySelectionFilterPromise = Promise.resolve(
          element.getEventsBySelectionFilterResponseForClickedPage
        );

        jest.runTimersToTime(element.AUTOREFRESH_DELAY);
        await getEventCountBySelectionFilterPromise;

        // at this time, the timer in the periodic refresher has triggered a getEventCountBySelectionFilter request which was resolved immediately. (see PeriodicRefresh prop 'periodicCallback' )
        // the request getEventsBySelctionFilter on onNewEventCountReceived has been resolved immediately as well, so the event list has been refreshed immediately

        // check that the page did not change
        expect(
          mountedWrapper
            .find('EventListMenu')
            .find('LogbookPager')
            .findWhere((w) => !!w.prop('selected'))
            .prop('page')
        ).toEqual(element.clickedPage);

        expect(
          mountedWrapper
            .find('EventList')
            .find('LogbookPager')
            .findWhere((w) => !!w.prop('selected'))
            .prop('page')
        ).toEqual(element.clickedPage);

        expect(mockedGetEventsBySelectionFilter).toHaveBeenCalledTimes(1);
      }

      next();
    }
  );
});

// describe("onNewEventUploaded", () => {

//     let mockedGetEvents = null;

//     beforeEach(() => {
//         // mock getEvents to avoid verbose http request errors
//         mockedGetEvents = jest.spyOn(LogbookContainer.prototype, 'getEvents').mockImplementation(() => null);
//     })
//     afterEach(() => {
//         mockedGetEvents.mockRestore()
//     })

//     function getWrapper() {
//         /* overrides default configuration */
//         GUI_CONFIG.mockReturnValue({
//             EVENTS_PER_PAGE: 2,
//             DEFAULT_SORTING_FILTER: {
//                 createdAt: -1  // temporary workaround. createdAt must be replaced by creationDate
//             },
//             AUTOREFRESH_DELAY: 100,

//         })

//         const middleware = [thunk];
//         const persistConfig = {
//             key: 'root',
//             storage,
//         }
//         const persistedReducer = persistReducer(persistConfig, reducer)
//         const store = createStore(
//             persistedReducer,
//             {
//                 user: {
//                     type: LOGGED_IN,
//                     username: 'username',
//                     sessionId: 'testSessionId'
//                 }
//             },
//             applyMiddleware(...middleware, logger, promise(), thunk)
//         )

//         return (Enzyme.shallow(<LogbookContainerInConnect
//             investigationId="testInvestigationId"
//             store={store}
//         />).dive())
//     }

//     function createEvent(id, title, creationDate) {
//         return ({
//             _id: id,
//             category: "comment",
//             content: [{
//                 format: 'plainText',
//                 text: 'this is a test'
//             }, {
//                 format: 'html',
//                 text: '<p> this is a test </p>'
//             }],
//             createdAt: creationDate, // was "2018-01-01T00:00:00.000Z"
//             creationDate: creationDate,
//             datasetId: null,
//             file: [],
//             fileSize: null,
//             filename: null,
//             investigationId: 'testInvestigationId',
//             machine: null,
//             previousVersionEvent: null,
//             software: null,
//             tag: [],
//             title: title,
//             type: "annotation",
//             updatedAt: "2018-01-01T00:00:00.000Z",
//             username: "mchaille"
//         })
//     }

//     function setupWrapper(wrapper, activePage, numberEventsFoundOnServer, numberEventsDownloaded) {
//         function getSliceForPageEvent() {
//             if (activePage === 1) {
//                 return actualfoundEvents.slice(0, 2)
//             }
//             if (activePage === 2) {
//                 return actualfoundEvents.slice(2, 4)
//             }
//             if (activePage === 3) {
//                 return actualfoundEvents.slice(4, 6)
//             }
//             if (activePage === 4) {
//                 return actualfoundEvents.slice(6, 8)
//             }
//             if (activePage === 5) {
//                 return actualfoundEvents.slice(8, 10)
//             }
//             if (activePage === 6) {
//                 return actualfoundEvents.slice(10)
//             }
//         }

//         function getSliceForDownloadedEvent() {
//             if (activePage === 1) {
//                 return actualfoundEvents.slice(0, 4)
//             }
//             if (activePage === 2) {
//                 return actualfoundEvents.slice(2, 6)
//             }
//             if (activePage === 3) {
//                 return actualfoundEvents.slice(4, 8)
//             }
//             if (activePage === 4) {
//                 return actualfoundEvents.slice(6, 10)
//             }
//             if (activePage === 5) {
//                 return actualfoundEvents.slice(8, 11)
//             }
//             if (activePage === 6) {
//                 return actualfoundEvents.slice(10)
//             }
//         }

//         function getDownloadedEventIndexes() {
//             if (activePage === 1) {
//                 return { start: 0, end: 3 }
//             }
//             if (activePage === 2) {
//                 return { start: 2, end: 5 }
//             }
//             if (activePage === 3) {
//                 return { start: 4, end: 7 }
//             }
//             if (activePage === 4) {
//                 return { start: 6, end: 9 }
//             }
//             if (activePage === 5) {
//                 return { start: 8, end: 10 }
//             }
//             if (activePage === 6) {
//                 return { start: 10, end: 10 }
//             }
//         }

//         /* creates numberEventsFoundOnServer events */
//         let actualfoundEvents = new Array();
//         for (let index = 1; index <= numberEventsFoundOnServer; index++) {
//             actualfoundEvents.push(createEvent(index, "title" + index, new Date('0' + index + ' Jan 2018 00:00:00 GMT')));
//         }

//         let actualActivePage = activePage;

//         // setup the starting state of the instance
//         wrapper.instance().setState({
//             activePage: actualActivePage,
//             downloadedEvents: getSliceForDownloadedEvent(),
//             downloadedEventIndexes: getDownloadedEventIndexes(),
//             foundEventCount: numberEventsFoundOnServer,
//             pageEvents: getSliceForPageEvent()
//         })
//         wrapper.update();

//         // mock the downloadEvent functions, because we want to see how it is called
//         wrapper.instance().getEvents = jest.fn();

//         wrapper.update();
//     }

//     it("sets the activepage to 1", () => {
//         let myEventContainerWrapper = getWrapper();

//         // create a wrapper that has received 4 events (while there are 11 found by the
//         // server) and currently displays page 2
//         let actualActivePage = 2;
//         setupWrapper(myEventContainerWrapper, actualActivePage, 11, 4)
//         expect(myEventContainerWrapper.instance().state.activePage).toBe(2);

//         // user has created  a new Event
//         myEventContainerWrapper.instance().onNewEventUploaded();

//         expect(myEventContainerWrapper.instance().state.activePage).toBe(1);
//     })

//     it("sets sets isNewEventVisible to false", () => {
//         let myEventContainerWrapper = getWrapper();

//         // create a wrapper that has received 4 events (while there are 11 found by the
//         // server) and currently displays page 2
//         let actualActivePage = 2;
//         setupWrapper(myEventContainerWrapper, actualActivePage, 11, 4)
//         myEventContainerWrapper.instance().state.isNewEventVisible = true;

//         // user has created  a new Event
//         myEventContainerWrapper.instance().onNewEventUploaded();

//         expect(myEventContainerWrapper.instance().state.isNewEventVisible).toBe(false);
//     })
// })

/**
 * Get a mounted wrapper
 */
function getMountedWrapper(propsObject) {
  const middleware = [thunk];
  const persistConfig = { key: 'root', storage };
  const persistedReducer = persistReducer(persistConfig, reducer);
  const store = createStore(
    persistedReducer,
    {
      user: {
        type: LOGGED_IN,
        username: 'username',
        sessionId: 'testSessionId',
      },
      releasedInvestigations: propsObject.releasedInvestigations
        ? { data: propsObject.releasedInvestigations }
        : { data: null },
    },
    applyMiddleware(...middleware, promise(), thunk)
  );
  const LogbookContainer = require('../../containers/Logbook/LogbookContainer')
    .default;
  return Enzyme.mount(
    <LogbookContainer
      investigationId={
        propsObject.investigationId
          ? propsObject.investigationId
          : 'testInvestigationId'
      }
    />,
    { context: { store } }
  );
}
