const React = require('react');
const {shallowWithIntl, mountWithIntl} = require('../../helpers/intl-helpers.jsx');
import configureStore from 'redux-mock-store';
const Navigation = require('../../../src/components/navigation/www/navigation.jsx');
const Registration = require('../../../src/components/registration/registration.jsx');
const sessionActions = require('../../../src/redux/session.js');

describe('Navigation', () => {
    const mockStore = configureStore();
    let store;

    beforeEach(() => {
        store = null;
        jest.useFakeTimers();
        jest.spyOn(global, 'setTimeout');
        jest.spyOn(global, 'clearTimeout');
    });

    const getNavigationWrapper = props => {
        const wrapper = shallowWithIntl(
            <Navigation
                {...props}
            />
            , {context: {store}}
        );
        return wrapper
            .dive() // unwrap redux connect(injectIntl(JoinFlow))
            .dive(); // unwrap injectIntl(JoinFlow)
    };

    test('when using old join flow, when registrationOpen is true, iframe shows', () => {
        store = mockStore({
            navigation: {
                registrationOpen: true,
                useScratch3Registration: false
            },
            session: {
                status: sessionActions.Status.FETCHED
            },
            messageCount: {
                messageCount: 0
            }
        });
        const navWrapper = getNavigationWrapper();
        expect(navWrapper.contains(<Registration />)).toEqual(true);
    });

    test('when using new join flow, when registrationOpen is true, iframe does not show', () => {
        store = mockStore({
            navigation: {
                registrationOpen: true,
                useScratch3Registration: true
            },
            session: {
                status: sessionActions.Status.FETCHED
            },
            messageCount: {
                messageCount: 0
            }
        });
        const navWrapper = getNavigationWrapper();
        expect(navWrapper.contains(<Registration />)).toEqual(false);
    });

    test('when using old join flow, clicking Join Scratch calls handleRegistrationRequested', () => {
        store = mockStore({
            navigation: {
                useScratch3Registration: false
            },
            session: {
                status: sessionActions.Status.FETCHED
            },
            messageCount: {
                messageCount: 0
            }
        });
        const props = {
            handleClickRegistration: jest.fn()
        };
        const navWrapper = getNavigationWrapper(props);
        const navInstance = navWrapper.instance();

        // simulate click, with mocked event
        navWrapper.find('a.registrationLink').simulate('click', {preventDefault () {}});
        expect(navInstance.props.handleClickRegistration).toHaveBeenCalled();
    });

    test('when using new join flow, clicking Join Scratch calls handleRegistrationRequested', () => {
        store = mockStore({
            navigation: {
                useScratch3Registration: true
            },
            session: {
                status: sessionActions.Status.FETCHED
            },
            messageCount: {
                messageCount: 0
            }
        });
        const props = {
            handleClickRegistration: jest.fn()
        };
        const navWrapper = getNavigationWrapper(props);
        const navInstance = navWrapper.instance();

        navWrapper.find('a.registrationLink').simulate('click', {preventDefault () {}});
        expect(navInstance.props.handleClickRegistration).toHaveBeenCalled();
    });

    test('Component sets up message polling when it mounts', () => {
        store = mockStore({
            navigation: {
                registrationOpen: false
            },
            messageCount: {
                messageCount: 5
            }
        });
        const props = {
            user: {
                thumbnailUrl: 'scratch.mit.edu',
                username: 'auser'
            },
            getMessageCount: jest.fn()
        };
        const intlWrapper = mountWithIntl(
            <Navigation
                {...props}
            />, {context: {store},
                childContextTypes: {store}
            });

        const navInstance = intlWrapper.children().find('Navigation')
            .instance();
        const twoMin = 2 * 60 * 1000;
        expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), twoMin);
        expect(navInstance.messageCountTimeoutId).not.toEqual(-1);
        // Advance timers passed the intial two minutes.
        jest.advanceTimersByTime(twoMin + 1);
        expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), twoMin * 2);
        expect(props.getMessageCount).toHaveBeenCalled();
        expect(navInstance.messageCountTimeoutId).not.toEqual(-1);
    });
    test('Component cancels timers when it unmounts', () => {
        store = mockStore({
            navigation: {
                registrationOpen: false
            },
            messageCount: {
                messageCount: 5
            }
        });
        const props = {
            user: {
                thumbnailUrl: 'scratch.mit.edu',
                username: 'auser'
            },
            getMessageCount: jest.fn()
        };
        const intlWrapper = mountWithIntl(
            <Navigation
                {...props}
            />, {context: {store},
                childContextTypes: {store}
            });

        const navInstance = intlWrapper.children().find('Navigation')
            .instance();
        const twoMin = 2 * 60 * 1000;
        expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), twoMin);
        expect(navInstance.messageCountTimeoutId).not.toEqual(-1);
        navInstance.componentWillUnmount();
        expect(clearTimeout).toHaveBeenCalledWith(expect.any(Number));
        expect(navInstance.messageCountTimeoutId).toEqual(-1);
    });

    test('pollForMessages polls for messages 5 times', () => {
        store = mockStore({
            navigation: {
                registrationOpen: false
            },
            messageCount: {
                messageCount: 5
            }
        });
        const props = {
            user: {
                thumbnailUrl: 'scratch.mit.edu',
                username: 'auser'
            },
            getMessageCount: jest.fn()
        };
        const intlWrapper = mountWithIntl(
            <Navigation
                {...props}
            />, {context: {store},
                childContextTypes: {store}
            });

        const navInstance = intlWrapper.children().find('Navigation')
            .instance();
        // Clear the timers and mocks because componentDidMount
        // has already called pollForMessages.
        jest.clearAllTimers();
        jest.clearAllMocks();
        let twoMinInMs = 2 * 60 * 1000; // 2 minutes in ms.
        navInstance.pollForMessages(twoMinInMs);

        expect(navInstance.messageCountTimeoutId).not.toEqual(-1);
        // Check that we set the timeout to backoff exponentially
        for (let count = 1; count < 5; ++count) {
            jest.advanceTimersByTime(twoMinInMs + 1);
            expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), twoMinInMs * 2);
            expect(props.getMessageCount).toHaveBeenCalledTimes(count);
            twoMinInMs = twoMinInMs * 2;
        }

        // Exhaust all timers (there shouldn't be any left)
        jest.runAllTimers();
        // We exponentially back off checking for messages, starting at 2 min
        // and stop after 32 minutes so it should happen 5 times total.
        expect(props.getMessageCount).toHaveBeenCalledTimes(5);
        // setTimeout happens 1 fewer since the original call to
        // pollForMessages isn't counted here.
        expect(setTimeout).toHaveBeenCalledTimes(4);
        expect(navInstance.messageCountTimeoutId).not.toEqual(-1);
    });
});