import React from 'react';
import { fireEvent, screen, waitFor } from '@testing-library/react';
import { Identity } from '@theorchard/suite-frontend';
import { createIdentity, renderInAppContext } from '@theorchard/suite-testing';
import { createMemoryHistory } from 'history';
import { Router } from 'react-router-dom';
import {
    AbacusContractAdvanceStatus,
    AbacusContractAdvanceMilestone,
} from 'src/apollo/definitions/globalTypes';
import * as contractAdvanceMutation from 'src/apollo/mutations/contract-advance';
import * as contractQuery from 'src/apollo/queries/contract';
import * as contractAdvanceQuery from 'src/apollo/queries/contract-advance';
import { AdvanceForm } from 'src/components/advance-form/advance-form';
import { CONTRACT_TYPES } from 'src/constants';
import * as validation from 'src/utils/form-validations/advance-form-validation';
import type { GetContractAdvanceQuery } from 'src/apollo/queries/contract-advance/__generated__/get-contract-advance';

type AbacusContractAdvance = GetContractAdvanceQuery['abacusContractAdvance'];

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

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

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

    let contractAdvanceMock: {
        abacusContractAdvance: AbacusContractAdvance;
    } = {
        abacusContractAdvance: {
            advanceDescription: 'Description',
            advanceStatus: AbacusContractAdvanceStatus.not_qualified,
            amount: 5,
            amountAfterWithholdingAndVat: 3,
            createdAt: '2020-01-01',
            contractAdvanceId: '3',
            contractId: '123',
            currencyCode: 'USD',
            milestone: AbacusContractAdvanceMilestone.contract_execution,
            milestoneDate: null,
            milestoneDescription: 'milestone description',
            note: null,
            usSourceIncomeRate: 100,
            referencePaymentType: null,
            vatAmount: 6,
            withholdingTaxAmount: 7,
        },
    };

    const history = createMemoryHistory();

    const render = (mockIdentity?: Partial<Identity>) =>
        renderInAppContext(
            <Router history={history}>
                {' '}
                <AdvanceForm />{' '}
            </Router>,
            {
                identity: createIdentity(mockIdentity),
            }
        );
    afterEach(jest.restoreAllMocks);

    beforeEach(() => {
        useContractSpy = jest
            .spyOn(contractQuery, 'useContractById')
            .mockReturnValue({
                data: contractMock,
                error: undefined,
                loading: false,
            });
        useContractAdvanceSpy = jest
            .spyOn(contractAdvanceQuery, 'useContractAdvanceById')
            .mockReturnValue({
                data: contractAdvanceMock,
                error: undefined,
                loading: false,
            });
        jest.spyOn(
            contractAdvanceMutation,
            'useUpdateContractAdvance'
        ).mockReturnValue(updateContractAdvanceMock);
    });

    it('renders', () => {
        const { container } = render();

        const paymentDescriptionInput = screen
            .getByTestId('paymentDescriptionTestId')
            .querySelector('input');
        const amount = screen
            .getByTestId('amountTestId')
            .querySelector('input');
        const milestoneDescription = screen
            .getByTestId('milestoneDescriptionTestId')
            .querySelector('textarea');
        expect(useContractSpy).toHaveBeenCalled();
        expect(useContractAdvanceSpy).toHaveBeenCalled();
        expect(paymentDescriptionInput).toHaveDisplayValue('Description');
        expect(amount).toHaveDisplayValue('5.00');
        expect(screen.getByText('USD (US Dollar)')).toBeDefined();
        expect(screen.getByText('Contract Execution')).toBeDefined();
        expect(milestoneDescription).toHaveDisplayValue(
            'milestone description'
        );
        expect(useContractSpy).toHaveBeenCalled();
        expect(useContractAdvanceSpy).toHaveBeenCalled();

        expect(
            container.getElementsByClassName('AdvanceFormFields').length
        ).toBe(1);
        expect(screen.getByText('Advance Creation Fields')).toBeDefined();
        expect(screen.getByText('Advance Payment Fields')).toBeDefined();
    });

    it('displays Update button for edit mode and triggers update method', () => {
        jest.spyOn(validation, 'advanceFormValidation').mockReturnValue({});
        render();
        const updateButton = screen.getByRole('button', { name: 'Update' });
        expect(updateButton).toBeDefined();
        fireEvent.click(updateButton);
        expect(updateContractAdvanceMock).toHaveBeenCalled();
    });

    it('redirects with params after editing the advance if milestone has been reached and the payment method is “payoneer via abacus"', async () => {
        contractAdvanceMock = {
            abacusContractAdvance: {
                ...contractAdvanceMock.abacusContractAdvance,
                contractAdvanceId: '1',
                milestoneDate: '2022-11-11',
            } as AbacusContractAdvance,
        };
        render();

        jest.spyOn(validation, 'advanceFormValidation').mockReturnValue({});

        expect(useContractSpy).toHaveBeenCalled();
        expect(useContractAdvanceSpy).toHaveBeenCalled();

        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 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&contractAdvanceId=555&referencePaymentTypeId=1'
            );
        });
    });

    describe('should disable save button', () => {
        it('should disable save button when payment is paid', () => {
            const _contractAdvanceMock = {
                abacusContractAdvance: {
                    ...contractAdvanceMock.abacusContractAdvance,
                    advanceStatus: AbacusContractAdvanceStatus.paid,
                    milestoneDate: null,
                },
            } as GetContractAdvanceQuery;

            useContractAdvanceSpy = jest
                .spyOn(contractAdvanceQuery, 'useContractAdvanceById')
                .mockReturnValue({
                    data: _contractAdvanceMock,
                    error: undefined,
                    loading: false,
                });

            render();

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

            expect(saveButton).toBeDisabled();
        });

        it('should disable save button when payment is pending', () => {
            const _contractAdvanceMock = {
                abacusContractAdvance: {
                    ...contractAdvanceMock.abacusContractAdvance,
                    advanceStatus: AbacusContractAdvanceStatus.pending_payment,
                    milestoneDate: null,
                },
            } as GetContractAdvanceQuery;

            useContractAdvanceSpy = jest
                .spyOn(contractAdvanceQuery, 'useContractAdvanceById')
                .mockReturnValue({
                    data: _contractAdvanceMock,
                    error: undefined,
                    loading: false,
                });

            render();

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

            expect(saveButton).toBeDisabled();
        });

        it('should disable save button when payments is in review', () => {
            const _contractAdvanceMock = {
                abacusContractAdvance: {
                    ...contractAdvanceMock.abacusContractAdvance,
                    advanceStatus: AbacusContractAdvanceStatus.in_review,
                    milestoneDate: null,
                },
            } as GetContractAdvanceQuery;

            useContractAdvanceSpy = jest
                .spyOn(contractAdvanceQuery, 'useContractAdvanceById')
                .mockReturnValue({
                    data: _contractAdvanceMock,
                    error: undefined,
                    loading: false,
                });

            render();

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

            expect(saveButton).toBeDisabled();
        });
    });
});
