import React from 'react';
import { fireEvent, screen, waitFor } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import { createMemoryHistory } from 'history';
import { Router } from 'react-router-dom';
import * as contractAdvanceMutation from 'src/apollo/mutations/contract-advance';
import * as contractQuery from 'src/apollo/queries/contract';
import * as exchangeRatesQuery from 'src/apollo/queries/exchange-rates';
import * as referencePaymentTypesQuery from 'src/apollo/queries/reference-payment-types';
import * as statementPeriodQuery from 'src/apollo/queries/statement-periods';
import { AdvanceForm } from 'src/components/advance-form/advance-form';
import { ADVANCES_CURRENCY_WARNING_ALERT, CONTRACT_TYPES } from 'src/constants';
import { getContractDetail } from 'src/urls/frontend-royalties';
import * as validation from 'src/utils/form-validations/advance-form-validation';

jest.mock('react-router-dom', () => ({
    ...jest.requireActual('react-router-dom'),
    useParams: jest.fn().mockReturnValue({ contractId: 123 }),
}));

describe('<AdvanceForm>', () => {
    let useContractSpy: any;
    const createContractMock = jest.fn().mockResolvedValue({});

    const contractMock = {
        abacusContract: {
            contractId: '123',
            contractType: CONTRACT_TYPES.DISTRIBUTION,
            contractName: 'Test contract',
            milestoneDate: '2024-02-01',
            account: {
                accountId: '123',
                accountName: 'test name',
                accountPaymentTerm: {
                    accountPaymentTermId: '654765',
                    currencyCode: 'AUD',
                    paymentEntity: {
                        paymentEntityName: 'AWAL-UK',
                        referencePaymentEntityId: '1',
                    },
                },
                accountPayee: {
                    accountPayeeId: '123',
                    payoneerPayeeId: null,
                    payoneerPayeeName: null,
                },
            },
        },
    };

    const statementPeriodsMock = {
        abacusStatementPeriods: {
            recentPeriods: [
                {
                    closedBy: null,
                    closedDate: null,
                    statementPeriodId: '1',
                    statementPeriodStatus: 'current',
                    statementPeriodName: 'test',
                },
            ],
        },
    };

    const exchangeRatesMock = {
        abacusExchangeRates: {
            items: [
                {
                    exchangeRateId: '1',
                    fromCurrencyCode: 'USD',
                    toCurrencyCode: 'GBP',
                    rate: '123.55',
                },
            ],
        },
    };

    let statementPeriodsSpy: any;
    const exchangeRatesSpy = jest.fn().mockReturnValue(exchangeRatesMock);

    const history = createMemoryHistory();
    const mockIdentity = {
        id: 'Jane User',
        features: {},
    };
    const render = (identity = mockIdentity) =>
        renderInAppContext(
            <Router history={history}>
                {' '}
                <AdvanceForm />
            </Router>,
            {
                identity,
            }
        );
    afterEach(jest.restoreAllMocks);

    beforeEach(() => {
        useContractSpy = jest
            .spyOn(contractQuery, 'useContractById')
            .mockReturnValue({
                data: contractMock,
                error: undefined,
                loading: false,
            });
        jest.spyOn(
            contractAdvanceMutation,
            'useCreateContractAdvance'
        ).mockReturnValue(createContractMock);
        statementPeriodsSpy = jest
            .spyOn(statementPeriodQuery, 'useStatementPeriodRecentPeriods')
            .mockReturnValue({
                data: statementPeriodsMock,
                error: undefined,
                loading: false,
            });
        jest.spyOn(exchangeRatesQuery, 'useGetExchangeRates')
            .mockReturnValueOnce({
                data: undefined,
                getExchangeRatesList: exchangeRatesSpy,
                error: undefined,
                loading: false,
            })
            .mockReturnValue({
                data: exchangeRatesMock,
                getExchangeRatesList: exchangeRatesSpy,
                error: undefined,
                loading: false,
            });
    });

    it('renders', () => {
        render();

        expect(useContractSpy).toHaveBeenCalled();
        expect(screen.getByText('Advance Creation Fields')).toBeDefined();
        expect(screen.getByText('Advance Payment Fields')).toBeDefined();
    });

    it('requests statement periods and the current exchange rates on load', () => {
        render();
        expect(statementPeriodsSpy).toHaveBeenCalled();
        expect(exchangeRatesSpy).toHaveBeenCalled();
    });

    it('redirects to contract detail page when cancel button is clicked', () => {
        render();
        const cancelButton = screen.getByRole('link', { name: 'Cancel' });
        expect(cancelButton.getAttribute('href')).toEqual(
            getContractDetail(123)
        );
    });

    it('renders error messages when the create button is clicked', () => {
        jest.spyOn(validation, 'advanceFormValidation').mockReturnValue({
            advanceDescription: 'Payment description can not be blank',
            amount: 'Amount can not be blank',
            currencyCode: '',
            milestone: '',
            milestoneDescription: '',
            milestoneDate: '',
        });
        render();

        const createButton = screen.getByRole('button', { name: 'Create' });
        fireEvent.click(createButton);
        expect(
            screen.getByText('Payment description can not be blank')
        ).toBeDefined();
        expect(screen.getByText('Amount can not be blank')).toBeDefined();
    });

    it('submits data when create button is clicked', () => {
        jest.spyOn(validation, 'advanceFormValidation').mockReturnValue({});
        render();

        const paymentDescription = screen
            .getByTestId('paymentDescriptionTestId')
            .querySelector('input');
        if (paymentDescription)
            fireEvent.change(paymentDescription, { target: { value: 'test' } });

        const amount = screen
            .getByTestId('amountTestId')
            .querySelector('input');
        if (amount) fireEvent.change(amount, { target: { value: '123.54' } });

        const currency = screen
            .getByTestId('currencyTestId')
            .querySelector('input');
        if (currency) fireEvent.change(currency, { target: { value: 'GBP' } });

        const milestone = screen
            .getByTestId('milestoneTestId')
            .querySelector('input');
        if (milestone)
            fireEvent.change(milestone, {
                target: { value: 'contract_exclusion' },
            });

        const milestoneDescription = screen
            .getByTestId('milestoneDescriptionTestId')
            .querySelector('input');
        if (milestoneDescription)
            fireEvent.change(milestoneDescription, {
                target: { value: 'test' },
            });

        const milestoneReached = screen
            .getByTestId('milestoneReachedTestId')
            .querySelector('input');
        if (milestoneReached)
            fireEvent.change(milestoneReached, { target: { value: 'no' } });

        const createButton = screen.getByRole('button', { name: 'Create' });
        fireEvent.click(createButton);
        expect(createContractMock).toHaveBeenCalled();
    });

    it('show currency warning for non matching payee currency', () => {
        render();

        const currency = screen
            .getByTestId('currencyTestId')
            .querySelector('input');
        if (currency) {
            fireEvent.change(currency, { target: { value: 'USD' } });
            fireEvent.keyDown(currency, {
                key: 'Enter',
                keyCode: 13,
                which: 13,
            });
        }
        expect(
            screen.getByText(ADVANCES_CURRENCY_WARNING_ALERT.text)
        ).toBeDefined();
    });

    it('hide currency warning for matching payee currency', () => {
        render();

        const currency = screen
            .getByTestId('currencyTestId')
            .querySelector('input');
        if (currency) {
            fireEvent.change(currency, { target: { value: 'AUD' } });
            fireEvent.keyDown(currency, {
                key: 'Enter',
                keyCode: 13,
                which: 13,
            });
        }
        expect(
            screen.queryByText(ADVANCES_CURRENCY_WARNING_ALERT.text)
        ).toBeNull();
    });

    it('redirects with params after creating the advance if milestone has been reached and the payment method is “payoneer via abacus"', async () => {
        jest.spyOn(validation, 'advanceFormValidation').mockReturnValue({});
        render();

        const paymentDescription = screen
            .getByTestId('paymentDescriptionTestId')
            .querySelector('input');
        if (paymentDescription)
            fireEvent.change(paymentDescription, {
                target: { value: 'test_payment_description' },
            });

        const amount = screen
            .getByTestId('amountTestId')
            .querySelector('input');
        if (amount) fireEvent.change(amount, { target: { value: '123.54' } });

        const currency = screen
            .getByTestId('currencyTestId')
            .querySelector('input');
        if (currency) {
            fireEvent.change(currency, { target: { value: 'USD' } });
            fireEvent.keyDown(currency, {
                key: 'Enter',
                keyCode: 13,
                which: 13,
            });
        }

        const milestone = screen
            .getByTestId('milestoneTestId')
            .querySelector('input');
        if (milestone) {
            fireEvent.change(milestone, { target: { value: 'delivery' } });
            fireEvent.keyDown(milestone, {
                key: 'Enter',
                keyCode: 13,
                which: 13,
            });
        }

        const milestoneDescription = screen
            .getByTestId('milestoneDescriptionTestId')
            .querySelector('textarea');
        if (milestoneDescription)
            fireEvent.change(milestoneDescription, {
                target: { value: 'test' },
            });

        const milestoneReached = screen
            .getByTestId('milestoneReachedTestId')
            .querySelector('input');
        if (milestoneReached) {
            fireEvent.change(milestoneReached, { target: { value: 'Yes' } });
            fireEvent.keyDown(milestoneReached, {
                key: 'Enter',
                keyCode: 13,
                which: 13,
            });
        }
        const milestoneDate = screen
            .getByTestId('milestoneDateTestId')
            .querySelector('input');
        if (milestoneDate) {
            fireEvent.change(milestoneDate, {
                target: { value: '2022-09-22' },
            });
            fireEvent.keyDown(milestoneDate, {
                key: 'Enter',
                keyCode: 13,
                which: 13,
            });
        }

        const createButton = screen.getByRole('button', {
            name: 'Save and create',
        });
        fireEvent.click(createButton);

        await waitFor(() => {
            expect(history.location.pathname).toBe('/payments/new');
            expect(history.location.search).toBe(
                '?selectTab=Specific Advance&contractId=123&referencePaymentTypeId=1'
            );
        });
    });

    it('resets checkboxes when payment type is changed', async () => {
        jest.spyOn(
            referencePaymentTypesQuery,
            'useReferencePaymentTypes'
        ).mockReturnValue({
            data: {
                abacusReferencePaymentTypes: {
                    items: [
                        {
                            isInternal: true,
                            notes: 'Payoneer via Abacus',
                            paymentService: 'Payoneer',
                            paymentType: 'advance',
                            referencePaymentTypeId: '1',
                        },
                        {
                            isInternal: false,
                            notes: 'Manually via Payoneer Console',
                            paymentService: 'Payoneer',
                            paymentType: 'advance',
                            referencePaymentTypeId: '2',
                        },
                        {
                            isInternal: false,
                            notes: 'Manually via SAP',
                            paymentService: 'sap',
                            paymentType: 'advance',
                            referencePaymentTypeId: '3',
                        },
                        {
                            isInternal: false,
                            notes: 'Manually via Convera',
                            paymentService: 'convera',
                            paymentType: 'advance',
                            referencePaymentTypeId: '4',
                        },
                    ],
                    totalCount: 0,
                },
            },
            loading: false,
            error: undefined,
        });

        render();

        const referencePaymentType = screen
            .getByTestId('referencePaymentType')
            .querySelector('input');
        const milestoneReached = screen.getAllByRole('combobox')[2];
        fireEvent.change(milestoneReached, { target: { value: 'Yes' } });
        if (referencePaymentType) {
            fireEvent.change(referencePaymentType, { target: { value: '2' } });
            fireEvent.keyDown(referencePaymentType, {
                key: 'Enter',
                keyCode: 13,
                which: 13,
            });
        }

        const approvalCheckbox = screen
            .getByTestId('approvalConfirmationTestId')
            .querySelector('input');

        if (approvalCheckbox) fireEvent.click(approvalCheckbox);
        expect(approvalCheckbox).toBeChecked();

        const paymentCheckbox = screen
            .getByTestId('paymentConfirmationTestId')
            .querySelector('input');
        fireEvent.click(paymentCheckbox as HTMLInputElement);
        expect(paymentCheckbox).toBeChecked();

        if (referencePaymentType) {
            fireEvent.change(referencePaymentType, { target: { value: '3' } });
            fireEvent.keyDown(referencePaymentType, {
                key: 'Enter',
                keyCode: 13,
                which: 13,
            });
        }

        expect(approvalCheckbox).not.toBeChecked();
        expect(paymentCheckbox).not.toBeChecked();
    });

    it('shows account contract information', () => {
        render();
        expect(screen.getByTestId('AccountContractInformation')).toBeDefined();
    });

    it('negates positive withholdingTaxAmount values', () => {
        render();
        const whtInput = screen
            .getByTestId('withholdingTaxAmount')
            .querySelector('input')!;
        fireEvent.change(whtInput, {
            target: { name: 'withholdingTaxAmount', value: '50' },
        });
        expect(whtInput).toHaveValue('-50');
    });

    it('caps usSourceIncomeRate at 100', () => {
        render();
        const rateInput = screen
            .getAllByTestId('usSourceIncomeRate')[0]
            .querySelector('input')!;
        fireEvent.change(rateInput, {
            target: { name: 'usSourceIncomeRate', value: '150' },
        });
        expect(rateInput).toHaveValue('100');
    });
});
