import React from 'react';
import { renderComponent } from 'lib/test-helpers/render-helper';
import { fireEvent, waitFor } from '@testing-library/react';
import Segment from 'src/utils/segment';
import {
    SEGMENT_CATEGORIES,
    SEGMENT_EVENTS
} from 'src/constants';
import { COLLABORATORS_QUERY } from 'src/pages/index-page/queries/collaborators';
import { AccountingPeriodType } from '../../../__definitions__/globalTypes';
import GenerateReportsFormConnected, { GenerateReportsForm, getPeriodChangeHandler } from '../generate-reports-form';
import * as storeUtils from '../../../store/store';
import {
    quarterAccountingPeriods,
    monthAccountingPeriods
} from '../../../__fixtures__/accountingPeriods';
import { collaborators } from '../../../__fixtures__/collaborators';
import { ACCOUNTING_PERIODS_QUERY } from '../queries/accounting-periods';
import { changeCollaboratorIdsAction } from '../../../actions/statement-generation';
import { COLLABORATOR_LIST_QUERY } from '../queries/collaborators-list';
import { TRIGGER_REPORT_GENERATION } from '../../../mutations/trigger-report-generation';
import { AccountingPeriods_accountingPeriods } from '../queries/__definitions__/AccountingPeriods';

jest.mock('../../../utils/date', () => ({
    getCurrentDateInFormat: jest.fn().mockReturnValue('19920201')
}));

const identity = {
    language: 'fr',
    contact: { contactEmail: 'carly.rae@jepsen.net' },
    vendor: { numberFormat: 'europe' }
};

const renderFooter: React.ComponentProps<
    typeof GenerateReportsFormConnected
>['renderFooter'] = ({ submitDisabled, handleSubmit }) => (
    <button
        type="button"
        data-testid="generate-page-generate-button"
        disabled={ submitDisabled }
        onClick={ handleSubmit }
    >
        Submit
    </button>
);

describe('<GenerateReportsForm>', () => {
    let segmentMock: jest.SpyInstance;

    beforeEach(() => {
        segmentMock = jest.spyOn(Segment, 'trackEvent');
    });

    afterEach(() => {
        segmentMock.mockRestore();
    });

    describe('when it is connected', () => {
        const store = storeUtils.createNewStore();
        test('renders the expected markup', async () => {
            const props = { store, renderFooter };
            const mocks = [
                {
                    request: {
                        query: ACCOUNTING_PERIODS_QUERY
                    },
                    result: {
                        data: {
                            accountingPeriods: null
                        }
                    }
                },
                {
                    request: {
                        query: COLLABORATOR_LIST_QUERY
                    },
                    result: {
                        data: {
                            collaboratorIds: null
                        }
                    }
                }
            ];

            const component = renderComponent({
                Component: GenerateReportsFormConnected,
                props,
                identity,
                mocks
            });
            await waitFor(() => {
                expect(component).toMatchSnapshot();
            });
        });
    });

    describe('when it is connected and has start and end periods', () => {
        const startPeriod = {
            firstPeriodId: 249,
            lastPeriodId: 249,
            periodType: 'MONTH',
            year: 2019,
            month: 9,
            quarter: 3
        };
        const endPeriod = {
            firstPeriodId: 250,
            lastPeriodId: 250,
            periodType: 'MONTH',
            year: 2019,
            month: 10,
            quarter: 4
        };
        const store = storeUtils.createNewStore({
            statementGeneration: { startPeriod, endPeriod }
        });
        test('renders the expected markup with the periods', async () => {
            const props = { store, renderFooter };
            const mocks = [
                {
                    request: {
                        query: ACCOUNTING_PERIODS_QUERY
                    },
                    result: {
                        data: {
                            accountingPeriods: null
                        }
                    }
                },
                {
                    request: {
                        query: COLLABORATOR_LIST_QUERY
                    },
                    result: {
                        data: {
                            collaboratorIds: null
                        }
                    }
                }
            ];

            const component = renderComponent({
                Component: GenerateReportsFormConnected,
                props,
                identity,
                mocks
            });
            await waitFor(() => {
                expect(component).toMatchSnapshot();
            });
        });
    });

    describe('when it is connected and has invalid start and end periods', () => {
        const startPeriod = {
            firstPeriodId: 205,
            lastPeriodId: 205,
            periodType: 'MONTH',
            year: 2016,
            month: 1,
            quarter: 1
        };
        const endPeriod = {
            firstPeriodId: 250,
            lastPeriodId: 250,
            periodType: 'MONTH',
            year: 2019,
            month: 10,
            quarter: 4
        };
        const store = storeUtils.createNewStore({
            statementGeneration: { startPeriod, endPeriod }
        });
        test('renders a report period length error', async () => {
            const props = { store, renderFooter };
            const mocks = [
                {
                    request: {
                        query: ACCOUNTING_PERIODS_QUERY
                    },
                    result: {
                        data: {
                            accountingPeriods: null
                        }
                    }
                },
                {
                    request: {
                        query: COLLABORATOR_LIST_QUERY
                    },
                    result: {
                        data: {
                            collaboratorIds: null
                        }
                    }
                }
            ];

            const component = renderComponent({
                Component: GenerateReportsFormConnected,
                props,
                identity,
                mocks
            });
            await waitFor(() => {
                expect(component).toMatchSnapshot();
            });
        });
    });

    describe('when only start date is set', () => {
        const dispatch = jest.fn();
        const mocks = [
            {
                request: {
                    query: ACCOUNTING_PERIODS_QUERY
                },
                result: {
                    data: {
                        accountingPeriods: monthAccountingPeriods
                    }
                }
            },
            {
                request: {
                    query: COLLABORATOR_LIST_QUERY
                },
                result: {
                    data: {
                        collaboratorIds: collaborators
                    }
                }
            }
        ];

        const props = {
            dispatch,
            history: { push: jest.fn() },
            startPeriod: monthAccountingPeriods[1],
            collaboratorIds: [],
            reportRunName: 'Report run 555',
            renderFooter,
        };

        test('has expected output', async () => {
            const component = renderComponent({
                Component: GenerateReportsForm,
                props,
                identity,
                mocks
            });

            expect(component).toMatchSnapshot();
            await waitFor(() => {});
            await waitFor(() => {});

        });
    });

    describe('when start date is selected first', () => {
        const dispatch = jest.fn();
        const mocks = [
            {
                request: {
                    query: ACCOUNTING_PERIODS_QUERY
                },
                result: {
                    data: {
                        accountingPeriods: monthAccountingPeriods
                    }
                }
            },
            {
                request: {
                    query: COLLABORATOR_LIST_QUERY
                },
                result: {
                    data: {
                        collaboratorIds: collaborators
                    }
                }
            }
        ];

        const props = {
            dispatch,
            history: { push: jest.fn() },
            startPeriod: monthAccountingPeriods[1],
            collaboratorIds: [],
            reportRunName: 'Report run 555',
            renderFooter,
        };

        test('has expected output', async () => {
            const component = renderComponent({
                Component: GenerateReportsForm,
                props,
                identity,
                mocks
            });

            await waitFor(() => {});
            await waitFor(() => {});

            const selectOptions = component.container.querySelectorAll('.Select-control');
            const selectEndOption = selectOptions[1];

            fireEvent.mouseDown(selectEndOption);

            await waitFor(() => {});
            expect(Segment.trackEvent).toHaveBeenCalledWith(
                SEGMENT_EVENTS.END_PERIOD_CLICK,
                SEGMENT_CATEGORIES.REPORTS
            );
            expect(component).toMatchSnapshot();
        });
    });

    describe('when end date is selected first', () => {
        const dispatch = jest.fn();
        const mocks = [
            {
                request: {
                    query: ACCOUNTING_PERIODS_QUERY
                },
                result: {
                    data: {
                        accountingPeriods: monthAccountingPeriods
                    }
                }
            },
            {
                request: {
                    query: COLLABORATOR_LIST_QUERY
                },
                result: {
                    data: {
                        collaboratorIds: collaborators
                    }
                }
            }
        ];

        const props = {
            dispatch,
            history: { push: jest.fn() },
            endPeriod: monthAccountingPeriods[1],
            collaboratorIds: [],
            reportRunName: 'Report run 555',
            renderFooter,
        };

        test('has expected output', async () => {
            const component = renderComponent({
                Component: GenerateReportsForm,
                props,
                identity,
                mocks
            });

            await waitFor(() => {});
            await waitFor(() => {});

            const selectOptions = component.container.querySelectorAll('.Select-control');
            const selectStartOption = selectOptions[0];

            fireEvent.mouseDown(selectStartOption);

            await waitFor(() => {});
            expect(Segment.trackEvent).toHaveBeenCalledWith(
                SEGMENT_EVENTS.START_PERIOD_CLICK,
                SEGMENT_CATEGORIES.REPORTS
            );
            expect(component).toMatchSnapshot();
        });
    });

});
describe('when an accounting period and collaborator is selected', () => {
    const mocks = [
        {
            request: {
                query: ACCOUNTING_PERIODS_QUERY
            },
            result: {
                data: {
                    accountingPeriods: monthAccountingPeriods
                }
            }
        },
        {
            request: {
                query: COLLABORATOR_LIST_QUERY
            },
            result: {
                data: {
                    collaboratorIds: collaborators
                }
            }
        }
    ];

    const props = {
        period: monthAccountingPeriods[1],
        collaboratorIds: [1],
        renderFooter,
        dispatch: jest.fn(),
    };
    test('renders the appropriate markup', async () => {
        const component = renderComponent({
            Component: GenerateReportsForm,
            props,
            identity,
            mocks
        });
        await waitFor(() => {});
        await waitFor(() => {});

        expect(component).toMatchSnapshot();
    });
});

describe('when accounting period and collaborator is selected and all is clicked', () => {
    const dispatch = jest.fn();
    const mocks = [
        {
            request: {
                query: ACCOUNTING_PERIODS_QUERY
            },
            result: {
                data: {
                    accountingPeriods: monthAccountingPeriods
                }
            }
        },
        {
            request: {
                query: COLLABORATOR_LIST_QUERY
            },
            result: {
                data: {
                    collaboratorIds: collaborators
                }
            }
        }
    ];

    const props = {
        period: monthAccountingPeriods[1],
        collaboratorIds: [1],
        dispatch,
        renderFooter,
    };
    let segmentMock: jest.SpyInstance;

    beforeEach(() => {
        segmentMock = jest.spyOn(Segment, 'trackEvent');
    });

    afterEach(() => {
        segmentMock.mockRestore();
    });

    test('clears the collaboratorIds and renders expected markup', async () => {
        const component = renderComponent({
            Component: GenerateReportsForm,
            props,
            identity,
            mocks,
        });

        const button = component.getByTestId('all-collaborators-button');

        await waitFor(() => {});

        fireEvent.click(button);

        expect(Segment.trackEvent).toHaveBeenCalledWith(
            SEGMENT_EVENTS.REPORT_ALL_CLICK,
            SEGMENT_CATEGORIES.REPORTS
        );

        expect(dispatch).toHaveBeenCalledWith(changeCollaboratorIdsAction([]));
        expect(component).toMatchSnapshot();
    });
});

describe('when the generate button is clicked', () => {
    let segmentMock: jest.SpyInstance;

    beforeEach(() => {
        segmentMock = jest.spyOn(Segment, 'trackEvent');
    });

    afterEach(() => {
        segmentMock.mockRestore();
    });
    describe('and report generation is not triggered successfully', () => {
        const dispatch = jest.fn();
        const mocks = [
            {
                request: {
                    query: ACCOUNTING_PERIODS_QUERY
                },
                result: {
                    data: {
                        accountingPeriods: monthAccountingPeriods
                    }
                }
            },
            {
                request: {
                    query: COLLABORATOR_LIST_QUERY
                },
                result: {
                    data: {
                        collaboratorIds: null
                    }
                }
            },
            {
                request: {
                    query: COLLABORATORS_QUERY
                },
                result: {
                    data: {
                        collaboratorIds: collaborators
                    }
                }
            },
            {
                request: {
                    query: TRIGGER_REPORT_GENERATION,
                    variables: {
                        firstPeriodId: 176,
                        lastPeriodId: 176,
                        periodName: 'août 2013',
                        clientEmail: 'carly.rae@jepsen.net',
                        reportRunName: 'Report run 555',
                        collaboratorIds: []
                    }
                },
                result: {
                    data: {
                        triggerCollaboratorsReportGeneration: {
                            accepted: false
                        }
                    }
                },
                error: new Error('Something went terribly wrong')
            }
        ];

        const props = {
            dispatch,
            history: { push: jest.fn() },
            period: monthAccountingPeriods[1],
            reportRunName: 'Report run 555',
            renderFooter,
        };

        test("doesn't push to history", async () => {
            const component = renderComponent({
                Component: GenerateReportsForm,
                props,
                identity,
                mocks
            });

            const generateButton = component
                .getByTestId('generate-page-generate-button');

            fireEvent.click(generateButton);

            await waitFor(() => {});

            expect(props.history.push).not.toHaveBeenCalled();
            expect(component).toMatchSnapshot();
        });
    });


    describe('and report generation is not triggered successfully with multicurrency periods', () => {
        const dispatch = jest.fn();
        const mocks = [
            {
                request: {
                    query: ACCOUNTING_PERIODS_QUERY
                },
                result: {
                    data: {
                        accountingPeriods: monthAccountingPeriods
                    }
                }
            },
            {
                request: {
                    query: COLLABORATOR_LIST_QUERY
                },
                result: {
                    data: {
                        collaboratorIds: null
                    }
                }
            },
            {
                request: {
                    query: COLLABORATORS_QUERY
                },
                result: {
                    data: {
                        collaboratorIds: collaborators
                    }
                }
            },
            {
                request: {
                    query: TRIGGER_REPORT_GENERATION,
                    variables: {
                        firstPeriodId: 176,
                        lastPeriodId: 176,
                        periodName: 'août 2013',
                        clientEmail: 'carly.rae@jepsen.net',
                        reportRunName: 'Report run 555',
                        collaboratorIds: []
                    }
                },
                result: {
                    data: {
                        triggerCollaboratorsReportGeneration: {
                            accepted: false
                        }
                    }
                },
                error: new Error('Something went terribly wrong'),
                errors: [
                    {
                        extensions: {
                            response: {
                                body: {
                                    code: 'different_currency_for_period'
                                }
                            }
                        }
                    }
                ]
            }
        ];

        const props = {
            dispatch,
            history: { push: jest.fn() },
            period: monthAccountingPeriods[1],
            reportRunName: 'Report run 555',
            renderFooter,
        };

        test("doesn't push to history", async () => {
            const component = renderComponent({
                Component: GenerateReportsForm,
                props,
                identity,
                mocks
            });

            const generateButton = component
                .getByTestId('generate-page-generate-button');

            fireEvent.click(generateButton);

            await waitFor(() => {});
            expect(props.history.push).not.toHaveBeenCalled();
            expect(component).toMatchSnapshot();
        });
    });
});

describe('when the generate button is clicked with selected Collaborators', () => {
    let segmentMock: jest.SpyInstance;

    beforeEach(() => {
        segmentMock = jest.spyOn(Segment, 'trackEvent');
    });

    afterEach(() => {
        segmentMock.mockRestore();
    });
    describe('and report generation is triggered successfully', () => {
        const dispatch = jest.fn();
        const mocks = [
            {
                request: {
                    query: ACCOUNTING_PERIODS_QUERY
                },
                result: {
                    data: {
                        accountingPeriods: monthAccountingPeriods
                    }
                }
            },
            {
                request: {
                    query: COLLABORATOR_LIST_QUERY
                },
                result: {
                    data: {
                        collaboratorIds: collaborators
                    }
                }
            },
            {
                request: {
                    query: COLLABORATORS_QUERY
                },
                result: {
                    data: {
                        collaboratorIds: collaborators
                    }
                }
            },
            {
                request: {
                    query: TRIGGER_REPORT_GENERATION,
                    variables: {
                        firstPeriodId: 176,
                        lastPeriodId: 176,
                        periodName: 'août 2013 - août 2013',
                        clientEmail: 'carly.rae@jepsen.net',
                        reportRunName: 'Report run 555',
                        collaboratorIds: [1, 2]
                    }
                },
                result: {
                    data: {
                        triggerCollaboratorsReportGeneration: {
                            accepted: true
                        }
                    }
                }
            }
        ];

        const props = {
            dispatch,
            onSuccess: jest.fn(),
            startPeriod: monthAccountingPeriods[1],
            endPeriod: monthAccountingPeriods[1],
            collaboratorIds: [1, 2],
            reportRunName: 'Report run 555',
            renderFooter,

        };

        test('shows loading and pushes the correct url and state to history', async () => {
            const component = renderComponent({
                Component: GenerateReportsForm,
                props,
                identity,
                mocks
            });

            const selectedCollaborators = component
                .getByTestId('selected-list-collaborators-button');

            await waitFor(() => {});

            const generateButton = component.getByTestId('generate-page-generate-button');

            fireEvent.click(selectedCollaborators);
            fireEvent.click(generateButton);

            expect(component).toMatchSnapshot();
            expect(Segment.trackEvent).toHaveBeenCalledWith(
                SEGMENT_EVENTS.REPORT_SELECT_CLICK,
                SEGMENT_CATEGORIES.REPORTS
            );
            expect(Segment.trackEvent).toHaveBeenCalledWith(
                SEGMENT_EVENTS.REPORT_GENERATE_BTN_CLICK,
                SEGMENT_CATEGORIES.REPORTS
            );
            await waitFor(() => {
                expect(props.onSuccess).toHaveBeenCalledWith();
            });
        });
    });

});

describe('when it has accountingPeriods and collaborator ids', () => {
    describe('with accountingPeriods of type quarter', () => {
        const mocks = [
            {
                request: {
                    query: ACCOUNTING_PERIODS_QUERY,
                },
                result: {
                    data: {
                        accountingPeriods: quarterAccountingPeriods,
                    },
                },
            },
            {
                request: {
                    query: COLLABORATOR_LIST_QUERY,
                },
                result: {
                    data: {
                        collaboratorIds: collaborators,
                    },
                },
            },
        ];

        const props = { renderFooter, dispatch: jest.fn() };

        test('renders the appropriate markup', async () => {
            const component = renderComponent({
                Component: GenerateReportsForm,
                props,
                identity,
                mocks,
            });
            await waitFor(() => {});
            await waitFor(() => {});

            expect(component).toMatchSnapshot();
        });
    });

    describe('with accountingPeriods of type month', () => {
        const mocks = [
            {
                request: {
                    query: ACCOUNTING_PERIODS_QUERY
                },
                result: {
                    data: {
                        accountingPeriods: monthAccountingPeriods
                    }
                }
            },
            {
                request: {
                    query: COLLABORATOR_LIST_QUERY
                },
                result: {
                    data: {
                        collaboratorIds: collaborators
                    }
                }
            }
        ];

        const props = { renderFooter, dispatch: jest.fn() };

        test('renders the appropriate markup', async () => {
            const component = renderComponent({
                Component: GenerateReportsForm,
                props,
                identity,
                mocks
            });
            await waitFor(() => {});
            await waitFor(() => {});

            expect(component).toMatchSnapshot();
        });
    });
});

describe('getPeriodChangeHandler', () => {
    const period: AccountingPeriods_accountingPeriods = {
        firstPeriodId: 241,
        lastPeriodId: 243,
        periodType: 'QUARTER' as AccountingPeriodType,
        year: 2019,
        month: null,
        quarter: 1,
        __typename: 'AccountingPeriod'
    };
    const accountingPeriods = [period];

    const dispatch = jest.fn();

    const action = { type: 'MOCK_ACTION' };
    const actionCreator = jest.fn(() => action);

    const handlePeriodChange = getPeriodChangeHandler(accountingPeriods, dispatch, actionCreator);

    handlePeriodChange(241);

    test(
        'the supplied action creator is called with the value and dispatched',
        () => {
            expect(actionCreator).toHaveBeenCalledWith(period);
            expect(dispatch).toHaveBeenCalledWith(action);
        }
    );
});
