import React, { ReducerState, ReducerAction } from 'react';
import { screen, fireEvent, waitFor } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import {
    contractTerm as distroContractTerms,
    NrContractTerms,
} from 'src/__fixtures__/graphql/contract-term';
import { AbacusContractType } from 'src/apollo/definitions/globalTypes';
import * as contractQuery from 'src/apollo/queries/contract';
import * as contractTermQuery from 'src/apollo/queries/contract-term';
import {
    CopyTermConditionsFormPopup,
    CopyTermConditionsFormPopupPropType,
} from 'src/components/contract-terms-form-nr/copy-term-conditions-form-popup';
import {
    ADD_CONTRACT_TERM_CONDITION,
    DELETE_ALL_TERM_CONDITIONS,
    EMPTY_TERM_CONDITIONS,
} from 'src/constants';
import { ContractTermContext } from 'src/contexts/contract-term-context';
import { initialTermState } from 'src/reducers/contract-term/contract-term-reducer';
import * as contractTerms from 'src/utils/contract-terms';

describe('<CopyTermConditionsFormPopup>', () => {
    describe('Copy NR Term Conditions', () => {
        let contractRequestSpy: any;
        let contractTermsRequestSpy: any;
        let formatConditionsSpy: any;

        const render = (
            contextValues: any,
            props: CopyTermConditionsFormPopupPropType
        ) =>
            renderInAppContext(
                <ContractTermContext.Provider value={contextValues}>
                    <CopyTermConditionsFormPopup {...props} />
                </ContractTermContext.Provider>
            );

        const mockContractResult = {
            abacusContracts: {
                totalCount: 1,
                items: [
                    {
                        contractId: '123',
                        contractName: 'Test contract name',
                        contractType:
                            AbacusContractType.NEIGHBOURING_RIGHTS.toLowerCase(),
                        account: {
                            accountId: '1',
                            accountName: 'Test account name',
                        },
                    },
                ],
            },
        };

        const props: CopyTermConditionsFormPopupPropType = {
            contractType: AbacusContractType.NEIGHBOURING_RIGHTS,
            isCopyConditionsModalOpen: true,
            setIsCopyConditionsModalOpen: jest.fn(),
        };

        beforeEach(() => {
            contractRequestSpy = jest
                .spyOn(contractQuery, 'useContractSearchQuery')
                .mockReturnValue(
                    async () =>
                        await Promise.resolve(
                            mockContractResult.abacusContracts.items
                        )
                );

            contractTermsRequestSpy = jest
                .spyOn(contractTermQuery, 'useContractTermsByContractId')
                .mockReturnValue({
                    data: NrContractTerms,
                    error: undefined,
                    loading: false,
                    getContractsTermsByContractId: jest
                        .fn()
                        .mockReturnValue(NrContractTerms),
                });

            formatConditionsSpy = jest
                .spyOn(contractTerms, 'formatConditions')
                .mockReturnValue({
                    countries: [],
                    stores: [],
                    transactionTypes: [],
                });
        });

        const contextValues = {
            state: {
                ...initialTermState,
                contract: {
                    contractId: '1',
                    contractName:
                        'AGR225807 - Gottwald, Lukasz Neighbouring Right',
                },
            },
            dispatch: jest.fn(),
        };

        it('renders <CopyTermConditionsFormPopup>', async () => {
            render(contextValues, props);

            expect(
                screen.getByText('Copy Term Conditions From Another Term')
            ).toBeDefined();

            expect(screen.getByText('From Contract ...')).toBeDefined();
            expect(
                screen.getByText(
                    '(Current) AGR225807 - Gottwald, Lukasz Neighbouring Right - 1'
                )
            ).toBeDefined();

            expect(screen.getByText('From Term ...')).toBeDefined();
        });

        it('searches contracts', async () => {
            render(contextValues, props);

            const contractSearchSelect = screen.getAllByRole('combobox')[0];
            fireEvent.change(contractSearchSelect, {
                target: { value: 'Test' },
            });
            await waitFor(() => {
                expect(
                    screen.getByText('Test contract name - 123')
                ).toBeDefined();
            });
            expect(contractRequestSpy).toHaveBeenCalled();
        });

        it('renders contract terms dropdown', async () => {
            const updatedContextValues = {
                state: {
                    ...initialTermState,
                    contract: {
                        contractId: '123',
                        contractName: 'Test contract name',
                    },
                },
                dispatch: jest.fn(),
            };
            render(updatedContextValues, props);

            fireEvent.click(screen.getByTestId('SuiteSelectInput'));
            expect(
                screen.getByText(
                    'NR1050 - Test Schedule Only Contract Term - 1'
                )
            ).toBeDefined();

            expect(
                screen.getByText(
                    'NR1051 - Test Contributor Only Contract Term - 2'
                )
            ).toBeDefined();
            expect(contractTermsRequestSpy).toHaveBeenCalled();
        });

        it('closes modal popup when isCopyConditionsModalOpen is false', async () => {
            const updatedProps = {
                ...props,
                isCopyConditionsModalOpen: false,
                setIsCopyConditionsModalOpen: jest.fn(),
            };
            render(contextValues, updatedProps);
            expect(
                screen.queryByText('Copy Term Conditions From Another Term')
            ).toBeNull();
        });

        it(`overwrites existing term conditions on click of "Update" button`, async () => {
            const updatedContextValues = {
                state: {
                    ...initialTermState,
                    contract: {
                        contractId: '123',
                        contractName: 'Test contract name',
                    },
                },
                dispatch: jest.fn(),
            };
            render(updatedContextValues, props);

            fireEvent.click(screen.getByTestId('SuiteSelectInput'));

            const searchInput = await screen.findByTestId(
                'SuiteListViewFilterInput'
            );
            fireEvent.change(searchInput, { target: { value: 'NR1050' } });
            fireEvent.keyDown(searchInput, {
                key: 'Enter',
                keyCode: 13,
                which: 13,
            });

            const updateButton = screen.getByRole('button', { name: 'Update' });
            fireEvent.click(updateButton);

            expect(updatedContextValues.dispatch).toHaveBeenCalledWith({
                type: DELETE_ALL_TERM_CONDITIONS,
                deleteTermConditions: [],
            });
            expect(updatedContextValues.dispatch).toHaveBeenCalledWith({
                type: EMPTY_TERM_CONDITIONS,
            });
            expect(formatConditionsSpy).toHaveBeenCalled();
            expect(updatedContextValues.dispatch).toHaveBeenCalledWith({
                type: ADD_CONTRACT_TERM_CONDITION,
                condition: {
                    contractTermConditionName: 'Test Contract Term Condition',
                    conditions: {
                        countries: [],
                        stores: [],
                        transactionTypes: [],
                    },
                    priority: 1,
                    commission: '49.5',
                },
            });
        });

        it('displays validation error when present', () => {
            const updatedContextValues = {
                state: {
                    ...initialTermState,
                    errors: { contract: 'Say please' },
                },
                dispatch: jest.fn(),
            };
            render(updatedContextValues, props);

            const updateButton = screen.getAllByRole('button')[1];
            fireEvent.click(updateButton);

            expect(screen.getByText('Say please')).toBeDefined();
        });
    });

    describe('Copy Distro Term Conditions', () => {
        let contractRequestSpy: any;
        let contractTermsRequestSpy: any;

        const defaultContextValues = {
            state: initialTermState,
            dispatch: jest.fn(),
        };

        const defaultProps: CopyTermConditionsFormPopupPropType = {
            contractType: AbacusContractType.DISTRIBUTION,
            isCopyConditionsModalOpen: true,
            setIsCopyConditionsModalOpen: jest.fn(),
        };

        const render = (
            contextValues: {
                state: ReducerState<any>;
                dispatch: ReducerAction<any>;
            } = defaultContextValues,
            props: CopyTermConditionsFormPopupPropType = defaultProps
        ) =>
            renderInAppContext(
                <ContractTermContext.Provider value={contextValues}>
                    <CopyTermConditionsFormPopup {...props} />
                </ContractTermContext.Provider>
            );

        const mockContractResult = {
            abacusContracts: {
                totalCount: 1,
                items: [distroContractTerms.abacusContractTerm.contract],
            },
        };

        const mockContractTermsResult = {
            abacusContractTerms: [distroContractTerms.abacusContractTerm],
        };

        beforeEach(() => {
            contractRequestSpy = jest
                .spyOn(contractQuery, 'useContractSearchQuery')
                .mockReturnValue(
                    async () =>
                        await Promise.resolve(
                            mockContractResult.abacusContracts.items
                        )
                );

            contractTermsRequestSpy = jest
                .spyOn(contractTermQuery, 'useContractTermsByContractId')
                .mockReturnValue({
                    data: mockContractTermsResult,
                    error: undefined,
                    loading: false,
                    getContractsTermsByContractId: jest
                        .fn()
                        .mockReturnValue(mockContractTermsResult),
                });
        });

        it('renders <CopyTermConditionsFormPopup/>', () => {
            render();

            const popupHeader = screen.getByRole('heading', { level: 3 });
            const contractLabel = screen.getByText('Contract');
            const termLabel = screen.getByText('Term');
            const copyBtn = screen.getByRole('button', { name: 'Copy' });

            expect(popupHeader).toBeDefined();
            expect(contractLabel).toBeDefined();
            expect(termLabel).toBeDefined();
            expect(copyBtn).toBeDefined();
        });

        it('searches distro contracts', async () => {
            const { contractName, contractId } =
                distroContractTerms.abacusContractTerm.contract;

            render();

            const contractSearchSelect = screen.getAllByRole('combobox')[0];
            fireEvent.change(contractSearchSelect, {
                target: { value: 'Test' },
            });

            await waitFor(() => {
                expect(
                    screen.getByText(`${contractName} - ${contractId}`)
                ).toBeDefined();
            });
            expect(contractRequestSpy).toHaveBeenCalled();
        });

        it('populates contract term dropdown select when a contract is selected', async () => {
            const { contractName, contractId } =
                distroContractTerms.abacusContractTerm.contract;

            render();

            const contractSearchSelect = screen.getAllByRole('combobox')[0];
            fireEvent.change(contractSearchSelect, {
                target: { value: 'Test' },
            });

            await waitFor(() => {
                expect(
                    screen.getByText(`${contractName} - ${contractId}`)
                ).toBeDefined();
            });

            const selectedContract = screen.getByText(
                `${contractName} - ${contractId}`
            );
            fireEvent.click(selectedContract);

            expect(contractTermsRequestSpy).toHaveBeenCalled();
        });

        it('disables the "Copy" button and contract term select by default', () => {
            render();

            const copyBtn = screen.getByRole('button', { name: 'Copy' });
            expect(copyBtn).toBeDisabled();

            const contractTermSelect = screen.getByTestId('SuiteSelectInput');
            expect(contractTermSelect).toHaveClass('disabled');
        });

        it('enables the "Copy" button when a contract term is selected', async () => {
            const {
                contractTermId,
                contractTermName,
                contract: { contractId, contractName },
            } = distroContractTerms.abacusContractTerm;

            render();

            const contractSearchSelect = screen.getAllByRole('combobox')[0];
            fireEvent.change(contractSearchSelect, {
                target: { value: 'Test' },
            });

            await waitFor(() => {
                expect(
                    screen.getByText(`${contractName} - ${contractId}`)
                ).toBeDefined();
            });

            const selectedContract = screen.getByText(
                `${contractName} - ${contractId}`
            );
            fireEvent.click(selectedContract);

            const contractTermLabel = `${contractTermName} - ${contractTermId}`;
            fireEvent.click(screen.getByTestId('SuiteSelectInput'));

            await waitFor(() => {
                expect(screen.getByText(contractTermLabel)).toBeDefined();
            });

            const selectedTerm = screen.getByText(contractTermLabel);
            fireEvent.click(selectedTerm);

            const copyBtn = screen.getByRole('button', { name: 'Copy' });
            expect(copyBtn).not.toBeDisabled();
        });

        it('copies conditions when "Copy" button is clicked', async () => {
            const {
                contractTermId,
                contractTermName,
                contract: { contractId, contractName },
            } = distroContractTerms.abacusContractTerm;

            render();

            const contractSearchSelect = screen.getAllByRole('combobox')[0];
            fireEvent.change(contractSearchSelect, {
                target: { value: 'Test' },
            });

            await waitFor(() => {
                expect(
                    screen.getByText(`${contractName} - ${contractId}`)
                ).toBeDefined();
            });

            const selectedContract = screen.getByText(
                `${contractName} - ${contractId}`
            );
            fireEvent.click(selectedContract);

            const contractTermLabel = `${contractTermName} - ${contractTermId}`;
            fireEvent.click(screen.getByTestId('SuiteSelectInput'));

            await waitFor(() => {
                expect(screen.getByText(contractTermLabel)).toBeDefined();
            });

            const selectedTerm = screen.getByText(contractTermLabel);
            fireEvent.click(selectedTerm);

            const copyBtn = screen.getByRole('button', { name: 'Copy' });
            fireEvent.click(copyBtn);

            expect(defaultContextValues.dispatch).toHaveBeenCalled();
        });
    });
});
