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 {
    getOption,
    openSelect,
    queryInputTag,
    selectOption,
    SUITE_FILTER_INPUT,
    SUITE_MULTI_SELECT_TEST_ID,
} from 'lib/test-utils/select';
import { Router } from 'react-router-dom';
import { contractAdvancesQualifiedList } from 'src/__fixtures__/graphql/contract-advances';
import paymentGroups from 'src/__fixtures__/graphql/payment-group-response.json';
import { referencePaymentEntities } from 'src/__fixtures__/graphql/reference-payment-entity';
import * as contractAdvanceMutations from 'src/apollo/mutations/contract-advance';
import * as customPaymentMutations from 'src/apollo/mutations/custom-payment/create-custom-payment';
import * as paymentGroupMutations from 'src/apollo/mutations/payment-group';
import * as paymentGroupPaymentMutations from 'src/apollo/mutations/payment-group-payment';
import { useAccountDetailsForPayment } from 'src/apollo/queries/account';
import * as contractQuery from 'src/apollo/queries/contract';
import * as contractAdvanceQuery from 'src/apollo/queries/contract-advance';
import * as paymentGroupQuery from 'src/apollo/queries/payment-group';
import * as refPaymentEntitiesQuery from 'src/apollo/queries/reference-payment-entity';
import {
    useStatementPeriodCurrentPeriod,
    useStatementPeriodsList,
} from 'src/apollo/queries/statement-periods';
import * as customPaymentTestHelpers from 'src/components/payment-group-form/custom-payment/__tests__/helpers';
import PaymentGroupForm from 'src/components/payment-group-form/payment-group-form';
import {
    EXISTING_GROUP,
    SPECIFIC_ADVANCE,
    SPECIFIC_GROUP,
    CUSTOM_PAYMENT,
    USER_FEATURES,
} from 'src/constants';
import { getCustomPaymentDetail } from 'src/urls/frontend-royalties';
import type { GetPaymentGroupsQuery } from 'src/apollo/queries/__generated__/payment-group';
import type { GetContractAdvancesQualifiedQuery } from 'src/apollo/queries/contract-advance/__generated__/get-contract-advances-qualified-list';

const mockGetBaseAccounts = jest.fn();
const mockUseAccountSearchQuery = jest.fn();

jest.mock('src/apollo/queries/account', () => ({
    ...jest.requireActual('src/apollo/queries/account'),
    useAccountDetailsForPayment: jest.fn(),
    useBaseAccountsSearchQuery: () => mockGetBaseAccounts,
    useAccountSearchQuery: () => mockUseAccountSearchQuery,
}));

jest.mock('src/apollo/queries/statement-periods', () => ({
    ...jest.requireActual('src/apollo/queries/statement-periods'),
    useStatementPeriodCurrentPeriod: jest.fn(),
    useStatementPeriodsList: jest.fn(),
}));

// The custom-payment tests drive the full multi-step form (select account,
// currency, contract, fill several fields, submit) through many sequential
// userEvent interactions, which legitimately run several seconds in jsdom and
// exceed the 5000ms default under parallel-suite CPU contention. Raise the
// ceiling for the whole suite so these long-running flows do not flake in CI.
jest.setTimeout(20000);

describe('<PaymentGroupForm />', () => {
    const addPaymentGroup = jest.fn().mockResolvedValue({});
    const addPaymentGroupPayment = jest.fn().mockResolvedValue({});
    const createCustomPayment = jest.fn().mockResolvedValue({});

    const render = (identity?: Identity) =>
        renderInAppContext(<PaymentGroupForm />, { identity });

    function setDefaultMocks() {
        (useStatementPeriodCurrentPeriod as jest.Mock).mockImplementation(
            () => ({
                data: null,
                loading: false,
                error: null,
            })
        );
        (useStatementPeriodsList as jest.Mock).mockImplementation(() => ({
            data: null,
            loading: false,
            error: null,
        }));
        (useAccountDetailsForPayment as jest.Mock).mockImplementation(() => ({
            data: null,
            loading: false,
            error: null,
        }));
        mockGetBaseAccounts.mockImplementation(
            async () => await Promise.resolve({})
        );
        mockUseAccountSearchQuery.mockImplementation(
            async () => await Promise.resolve({})
        );
    }

    afterEach(() => {
        jest.restoreAllMocks();
        setDefaultMocks();
    });

    beforeEach(() => {
        jest.spyOn(paymentGroupQuery, 'usePaymentGroupsList').mockReturnValue({
            data: paymentGroups as GetPaymentGroupsQuery,
            loading: false,
            error: undefined,
            refetch: jest.fn(),
        });
        jest.spyOn(
            paymentGroupMutations,
            'useCreatePaymentGroup'
        ).mockReturnValue(addPaymentGroup);
        jest.spyOn(
            paymentGroupPaymentMutations,
            'useCreatePaymentGroupPayment'
        ).mockReturnValue(addPaymentGroupPayment);
        jest.spyOn(
            refPaymentEntitiesQuery,
            'useReferencePaymentEntities'
        ).mockReturnValue({
            data: referencePaymentEntities,
            loading: false,
            error: undefined,
        });
        jest.spyOn(
            customPaymentMutations,
            'useCreateCustomPayment'
        ).mockReturnValue({
            createCustomPayment,
            loading: false,
        });

        setDefaultMocks();
    });

    it('renders a button group for payment groups', () => {
        render();

        const existingGroupButton = screen.getByText(EXISTING_GROUP);
        const specificGroupButton = screen.getByText(SPECIFIC_GROUP);

        expect(existingGroupButton.classList).toContain('btn');
        expect(specificGroupButton.classList).toContain('btn');
    });

    describe('Existing payment group', () => {
        it('renders when the "Existing Group" button is clicked', () => {
            render();

            const existingGroupButton = screen.getByText(EXISTING_GROUP);
            fireEvent.click(existingGroupButton);

            expect(screen.getByText('Group Name')).toBeDefined();
            expect(screen.getByText('Payment Name')).toBeDefined();
            expect(existingGroupButton.classList).toContain('active');
        });
    });

    describe('Specific account payment group', () => {
        describe('multi-account selection', () => {
            beforeEach(() => {
                jest.clearAllMocks();
            });

            it('renders multi-select account dropdown', () => {
                render();

                const specificGroupButton = screen.getByText(SPECIFIC_GROUP);
                fireEvent.click(specificGroupButton);

                expect(screen.getByTestId('input-payment-name')).toBeDefined();
                expect(screen.getByText('Search for Accounts')).toBeDefined();
                expect(screen.getByText('Account(s)')).toBeInTheDocument();
                expect(
                    screen.queryByText('Account Name')
                ).not.toBeInTheDocument();
                expect(specificGroupButton.classList).toContain('active');
            });

            it('should call useCreatePaymentGroup with multiple account ids', async () => {
                mockGetBaseAccounts.mockImplementation(() =>
                    Promise.resolve(
                        customPaymentTestHelpers.baseAccountsMockResult
                    )
                );

                const inputValue = ' 1, 2 ';
                const options = customPaymentTestHelpers.accountDropdownOptions;

                render();

                const specificGroupButton = screen.getByText(SPECIFIC_GROUP);
                fireEvent.click(specificGroupButton);

                await openSelect(SUITE_MULTI_SELECT_TEST_ID);

                const searchInput = screen.getByTestId(SUITE_FILTER_INPUT);

                fireEvent.change(searchInput, {
                    target: { value: inputValue },
                });

                await waitFor(() => {
                    expect(mockGetBaseAccounts).toHaveBeenCalledWith(
                        undefined,
                        ['1', '2'],
                        100,
                        0
                    );
                });

                options.forEach(({ label }) => {
                    expect(getOption({ label })).toBeInTheDocument();
                });

                selectOption(options[0]);

                await waitFor(() => {
                    expect(
                        queryInputTag({ label: options[0].label })
                    ).toBeInTheDocument();
                });

                fireEvent.change(searchInput, {
                    target: { value: inputValue },
                });

                await waitFor(() => {
                    expect(mockGetBaseAccounts).toHaveBeenCalledWith(
                        undefined,
                        ['1', '2'],
                        100,
                        0
                    );
                });

                await waitFor(() => {
                    const { label } = options[1];

                    expect(getOption({ label })).toBeInTheDocument();
                });

                selectOption(options[1]);

                await waitFor(() => {
                    expect(
                        queryInputTag({ label: options[0].label })
                    ).toBeInTheDocument();
                    expect(
                        queryInputTag({ label: options[1].label })
                    ).toBeInTheDocument();
                });

                const createButton = screen.getByText('Create');
                const paymentNameInput =
                    screen.getByTestId('input-payment-name');

                expect(createButton).toBeInTheDocument();
                expect(createButton).toBeDisabled();
                expect(paymentNameInput).toBeInTheDocument();

                fireEvent.change(paymentNameInput, {
                    target: { value: 'Test Payment Name' },
                });

                await waitFor(() => {
                    expect(createButton).not.toBeDisabled();
                });

                fireEvent.click(createButton);

                await waitFor(() => {
                    expect(addPaymentGroup).toHaveBeenCalledWith({
                        variables: {
                            groupCriteria: {
                                accountIds: ['1', '2'],
                            },
                            groupName: 'Multiple accounts',
                            paymentName: 'Test Payment Name',
                        },
                    });
                });
            });

            it('should call useCreatePaymentGroup with single account id', async () => {
                mockGetBaseAccounts.mockImplementation(() =>
                    Promise.resolve(
                        customPaymentTestHelpers.baseAccountsMockResult
                    )
                );

                const inputValue = ' 1, 2 ';
                const options = customPaymentTestHelpers.accountDropdownOptions;

                render();

                const specificGroupButton = screen.getByText(SPECIFIC_GROUP);
                fireEvent.click(specificGroupButton);

                await openSelect(SUITE_MULTI_SELECT_TEST_ID);

                const searchInput = screen.getByTestId(SUITE_FILTER_INPUT);

                fireEvent.change(searchInput, {
                    target: { value: inputValue },
                });

                await waitFor(() => {
                    expect(mockGetBaseAccounts).toHaveBeenCalledWith(
                        undefined,
                        ['1', '2'],
                        100,
                        0
                    );
                });

                options.forEach(({ label }) => {
                    expect(getOption({ label })).toBeInTheDocument();
                });

                selectOption(options[0]);

                await waitFor(() => {
                    expect(
                        queryInputTag({ label: options[0].label })
                    ).toBeInTheDocument();
                });

                const createButton = screen.getByText('Create');
                const paymentNameInput =
                    screen.getByTestId('input-payment-name');

                expect(createButton).toBeInTheDocument();
                expect(createButton).toBeDisabled();
                expect(paymentNameInput).toBeInTheDocument();

                fireEvent.change(paymentNameInput, {
                    target: { value: 'Test Payment Name' },
                });

                await waitFor(() => {
                    expect(createButton).not.toBeDisabled();
                });

                fireEvent.click(createButton);

                await waitFor(() => {
                    expect(addPaymentGroup).toHaveBeenCalledWith({
                        variables: {
                            groupCriteria: {
                                accountIds: [options[0].value],
                            },
                            groupName: options[0].label,
                            paymentName: 'Test Payment Name',
                        },
                    });
                });
            });
        });
    });

    describe('Specific advance payment group', () => {
        it('renders when the "Specific Advance button is clicked', () => {
            render();

            const specificAdvanceButton = screen.getByText(SPECIFIC_ADVANCE);
            fireEvent.click(specificAdvanceButton);

            expect(screen.getByText('Contract')).toBeDefined();
            expect(
                screen.getByText('Search By Contract Name or ID')
            ).toBeDefined();
            expect(specificAdvanceButton.classList).toContain('active');
        });

        it('redirects to payment page after creation', async () => {
            const mockContractResult = {
                abacusContracts: {
                    totalCount: 1,
                    items: [
                        {
                            contractId: '123',
                            contractName: 'Test contract name',
                            contractType: 'neighbouring_rights',
                            isExcludedFromAccountingRun: false,
                            isPrimaryContract: false,
                            account: {
                                accountId: '1',
                                accountName: 'Test account name',
                            },
                            runController: {
                                runControllerName: null,
                            },
                            lifecycle: null,
                        },
                    ],
                },
            };

            const mockActionStates = [
                {
                    abacusStateId: '1',
                    actionName: 'payment_eligibility',
                    actionStatus: 'approved',
                    message: 'test message',
                },
                {
                    abacusStateId: '2',
                    actionName: 'tax_eligibility',
                    actionStatus: 'complete',
                    message: 'test message',
                },
            ];
            jest.spyOn(contractQuery, 'useContractSearchQuery').mockReturnValue(
                async () =>
                    await Promise.resolve(
                        mockContractResult.abacusContracts.items
                    )
            );
            jest.spyOn(
                contractQuery,
                'useContractAccountPayeeActionStates'
            ).mockReturnValue({
                data: mockActionStates,
                loading: false,
                error: undefined,
                getActionStatesByContractId: jest.fn().mockResolvedValue({
                    data: mockActionStates,
                }),
            });
            jest.spyOn(
                contractAdvanceQuery,
                'useContractAdvancesQualified'
            ).mockReturnValue({
                data: contractAdvancesQualifiedList as GetContractAdvancesQualifiedQuery,
                loading: false,
                error: undefined,
            });
            jest.spyOn(
                contractAdvanceMutations,
                'useCreateWorksheetPaymentContractAdvance'
            ).mockReturnValue(
                jest.fn().mockResolvedValue({
                    data: {
                        createWorksheetPaymentContractAdvance: {
                            worksheetPaymentContractAdvanceId: 1,
                        },
                    },
                })
            );

            const history = createMemoryHistory();

            renderInAppContext(
                <Router history={history}>
                    <PaymentGroupForm />
                </Router>
            );

            await waitFor(() => {
                screen.getByText(SPECIFIC_ADVANCE);
            });
            const specificAdvanceButton = screen.getByText(SPECIFIC_ADVANCE);
            fireEvent.click(specificAdvanceButton);

            expect(screen.getByText('Contract')).toBeDefined();
            expect(
                screen.getByText('Search By Contract Name or ID')
            ).toBeDefined();
            expect(specificAdvanceButton.classList).toContain('active');

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

            const searchResulElement = screen.getByText(
                'Test contract name - 123'
            );
            fireEvent.click(searchResulElement);
            expect(screen.getByText('Select Unpaid Advance')).toBeDefined();
            const contractAdvancePendingDropdown = screen.getByText(
                'Test Advance 3 | $789,657,896.00'
            );

            fireEvent.click(contractAdvancePendingDropdown);

            if (contractAdvancePendingDropdown) {
                fireEvent.focus(contractAdvancePendingDropdown);
                fireEvent.keyDown(contractAdvancePendingDropdown, {
                    key: 'ArrowDown',
                    code: 40,
                });
            }

            const createButton = screen.getByText('Create');
            fireEvent.click(createButton);

            await waitFor(() => {
                expect(history.location.pathname).toBe(`/payment/advance/1`);
            });
        });

        it('trims whitespace from the payment name before submitting', async () => {
            const mockContractResult = {
                abacusContracts: {
                    totalCount: 1,
                    items: [
                        {
                            contractId: '123',
                            contractName: 'Test contract name',
                            contractType: 'neighbouring_rights',
                            isExcludedFromAccountingRun: false,
                            isPrimaryContract: false,
                            account: {
                                accountId: '1',
                                accountName: 'Test account name',
                            },
                            runController: {
                                runControllerName: null,
                            },
                            lifecycle: null,
                        },
                    ],
                },
            };

            const mockActionStates = [
                {
                    abacusStateId: '1',
                    actionName: 'payment_eligibility',
                    actionStatus: 'approved',
                    message: 'test message',
                },
                {
                    abacusStateId: '2',
                    actionName: 'tax_eligibility',
                    actionStatus: 'complete',
                    message: 'test message',
                },
            ];

            jest.spyOn(contractQuery, 'useContractSearchQuery').mockReturnValue(
                async () =>
                    await Promise.resolve(
                        mockContractResult.abacusContracts.items
                    )
            );
            jest.spyOn(
                contractQuery,
                'useContractAccountPayeeActionStates'
            ).mockReturnValue({
                data: mockActionStates,
                loading: false,
                error: undefined,
                getActionStatesByContractId: jest.fn().mockResolvedValue({
                    data: mockActionStates,
                }),
            });
            jest.spyOn(
                contractAdvanceQuery,
                'useContractAdvancesQualified'
            ).mockReturnValue({
                data: contractAdvancesQualifiedList as GetContractAdvancesQualifiedQuery,
                loading: false,
                error: undefined,
            });

            const mockCreateWorksheetPaymentContractAdvance = jest
                .fn()
                .mockResolvedValue({
                    data: {
                        createWorksheetPaymentContractAdvance: {
                            worksheetPaymentContractAdvanceId: 1,
                        },
                    },
                });
            jest.spyOn(
                contractAdvanceMutations,
                'useCreateWorksheetPaymentContractAdvance'
            ).mockReturnValue(mockCreateWorksheetPaymentContractAdvance);

            const history = createMemoryHistory();

            renderInAppContext(
                <Router history={history}>
                    <PaymentGroupForm />
                </Router>
            );

            await waitFor(() => {
                screen.getByText(SPECIFIC_ADVANCE);
            });
            fireEvent.click(screen.getByText(SPECIFIC_ADVANCE));

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

            fireEvent.click(screen.getByText('Test contract name - 123'));
            const contractAdvancePendingDropdown = screen.getByText(
                'Test Advance 3 | $789,657,896.00'
            );

            fireEvent.click(contractAdvancePendingDropdown);
            fireEvent.focus(contractAdvancePendingDropdown);
            fireEvent.keyDown(contractAdvancePendingDropdown, {
                key: 'ArrowDown',
                code: 40,
            });

            const paymentNameInput = screen.getByTestId('input-payment-name');
            fireEvent.change(paymentNameInput, {
                target: { value: '  Test Advance Name  ' },
            });

            fireEvent.click(screen.getByText('Create'));

            await waitFor(() => {
                expect(
                    mockCreateWorksheetPaymentContractAdvance
                ).toHaveBeenCalledWith({
                    variables: expect.objectContaining({
                        paymentName: 'Test Advance Name',
                    }),
                });
            });
        });
    });

    describe('Custom payment', () => {
        const identityWithCustomPayments = createIdentity();

        beforeEach(() => {
            jest.clearAllMocks();
            setDefaultMocks();
        });

        const baseSetup = async () => {
            mockGetBaseAccounts.mockImplementation(() =>
                Promise.resolve(customPaymentTestHelpers.baseAccountsMockResult)
            );

            (useStatementPeriodCurrentPeriod as jest.Mock).mockImplementation(
                () => customPaymentTestHelpers.currentPeriodMockResult
            );

            (useAccountDetailsForPayment as jest.Mock).mockImplementation(
                () => customPaymentTestHelpers.customPaymentAccountMockResult
            );

            (useStatementPeriodsList as jest.Mock).mockImplementation(
                () => customPaymentTestHelpers.statementPeriodsListMockResult
            );

            render(identityWithCustomPayments);

            const customPaymentButton = screen.getByText(CUSTOM_PAYMENT);

            fireEvent.click(customPaymentButton);
        };

        it('should render custom payment option', () => {
            render(identityWithCustomPayments);

            expect(screen.getByText(CUSTOM_PAYMENT)).toBeInTheDocument();
        });

        it('should render custom payment form when custom payment option is selected and fill the form', async () => {
            await baseSetup();

            const continueButton = await screen.findByText('Create');

            expect(continueButton).toHaveProperty('disabled', true);

            await customPaymentTestHelpers.goThroughSelectAccountStep(
                mockGetBaseAccounts
            );

            expect(useStatementPeriodCurrentPeriod).toHaveBeenCalled();
            expect(useAccountDetailsForPayment).toHaveBeenCalledWith(
                customPaymentTestHelpers.accountDropdownOptions[0].value
            );
            expect(
                screen.queryByTestId('SkeletonLoader')
            ).not.toBeInTheDocument();

            customPaymentTestHelpers.detailsControlsLabels.forEach(label => {
                expect(screen.getByText(label)).toBeInTheDocument();
            });

            customPaymentTestHelpers.goThroughSelectCurrencyStep();

            await customPaymentTestHelpers.goThroughSelectContractStep(true);

            await customPaymentTestHelpers.fillTextField({
                label: 'amount',
                value: '0.1',
            });

            await customPaymentTestHelpers.fillTextField({
                label: 'wht',
                value: '0.2',
            });

            await customPaymentTestHelpers.fillTextField({
                label: 'wht rate',
                value: '10',
            });

            await customPaymentTestHelpers.fillTextField({
                label: 'vat',
                value: '0.1',
            });

            await customPaymentTestHelpers.fillTextField({
                label: 'vat rate',
                value: '1.0',
            });

            await customPaymentTestHelpers.goThroughSelectActivityStatementPeriod(
                true
            );

            await customPaymentTestHelpers.fillTextField({
                label: 'Payment Name',
                value: '23',
            });

            await waitFor(() => {
                expect(continueButton).toHaveProperty('disabled', false);
            });

            fireEvent.click(continueButton);

            await waitFor(() => {
                expect(createCustomPayment).toHaveBeenCalledWith({
                    variables: {
                        input: {
                            accountId: '1',
                            activityStatementPeriodId: '265',
                            amount: '0.1',
                            amountAfterWithholdingAndVat: '0.4',
                            contractId: '102',
                            currencyCode: 'USD',
                            paymentName: '23',
                            statementPeriodId: '265',
                            vatAmount: '0.1',
                            vatRate: '1.0',
                            withholdingTaxAmount: '0.2',
                            withholdingTaxRate: '10',
                        },
                    },
                });
            });
        });

        it('should show error message when total payment amount is not greater than 0', async () => {
            await baseSetup();

            const continueButton = await screen.findByText('Create');

            expect(continueButton).toHaveProperty('disabled', true);

            await customPaymentTestHelpers.goThroughSelectAccountStep(
                mockGetBaseAccounts
            );

            await customPaymentTestHelpers.goThroughSelectCurrencyStep();

            await customPaymentTestHelpers.goThroughSelectContractStep(true);

            await customPaymentTestHelpers.fillTextField({
                label: 'amount',
                value: '0',
            });

            await customPaymentTestHelpers.fillTextField({
                label: 'wht',
                value: '-10',
            });

            await customPaymentTestHelpers.fillTextField({
                label: 'wht rate',
                value: '10',
            });

            await customPaymentTestHelpers.fillTextField({
                label: 'vat',
                value: '0.0',
            });

            await customPaymentTestHelpers.fillTextField({
                label: 'vat rate',
                value: '1.0',
            });

            await customPaymentTestHelpers.goThroughSelectActivityStatementPeriod(
                true
            );

            await customPaymentTestHelpers.fillTextField({
                label: 'Payment Name',
                value: '23',
            });

            await waitFor(() => {
                expect(continueButton).toHaveProperty('disabled', false);
            });

            fireEvent.click(continueButton);

            await waitFor(() => {
                expect(
                    screen.getByText(
                        'Total payment amount must be greater than 0.'
                    )
                ).toBeInTheDocument();
                expect(
                    screen.queryByText('Rate is required for WHT amount.')
                ).toBeNull();
                expect(
                    screen.queryByText('Rate is required for VAT amount.')
                ).toBeNull();
                expect(continueButton).toHaveProperty('disabled', true);
            });
        });

        it('should show errors for amounts without required rates', async () => {
            await baseSetup();

            const continueButton = await screen.findByText('Create');

            expect(continueButton).toHaveProperty('disabled', true);

            await customPaymentTestHelpers.goThroughSelectAccountStep(
                mockGetBaseAccounts
            );

            await customPaymentTestHelpers.goThroughSelectCurrencyStep();

            await customPaymentTestHelpers.goThroughSelectContractStep(true);

            await customPaymentTestHelpers.fillTextField({
                label: 'amount',
                value: '100',
            });

            await customPaymentTestHelpers.fillTextField({
                label: 'wht',
                value: '-10',
            });

            await customPaymentTestHelpers.fillTextField({
                label: 'vat',
                value: '0.1',
            });

            await customPaymentTestHelpers.goThroughSelectActivityStatementPeriod(
                true
            );

            await customPaymentTestHelpers.fillTextField({
                label: 'Payment Name',
                value: '23',
            });

            await waitFor(() => {
                expect(continueButton).toHaveProperty('disabled', false);
            });

            fireEvent.click(continueButton);

            await waitFor(() => {
                expect(
                    screen.queryByText(
                        'Total payment amount must be greater than 0.'
                    )
                ).toBeNull();
                expect(
                    screen.getByText('Rate is required for WHT amount.')
                ).toBeInTheDocument();
                expect(
                    screen.getByText('Rate is required for VAT amount.')
                ).toBeInTheDocument();
                expect(continueButton).toHaveProperty('disabled', true);
            });
        });

        it('show errors for rates without required amounts', async () => {
            await baseSetup();

            const continueButton = await screen.findByText('Create');

            expect(continueButton).toHaveProperty('disabled', true);

            await customPaymentTestHelpers.goThroughSelectAccountStep(
                mockGetBaseAccounts
            );

            await customPaymentTestHelpers.goThroughSelectCurrencyStep();

            await customPaymentTestHelpers.goThroughSelectContractStep(true);

            await customPaymentTestHelpers.fillTextField({
                label: 'amount',
                value: '100',
            });

            await customPaymentTestHelpers.fillTextField({
                label: 'WHT Rate',
                value: '10',
            });

            await customPaymentTestHelpers.fillTextField({
                label: 'VAT Rate',
                value: '0.1',
            });

            await customPaymentTestHelpers.goThroughSelectActivityStatementPeriod(
                true
            );

            await customPaymentTestHelpers.fillTextField({
                label: 'Payment Name',
                value: '23',
            });

            await waitFor(() => {
                expect(continueButton).toHaveProperty('disabled', false);
            });

            fireEvent.click(continueButton);

            await waitFor(() => {
                expect(
                    screen.getByText('WHT amount is required for WHT rate.')
                ).toBeInTheDocument();
                expect(
                    screen.getByText('VAT amount is required for VAT rate.')
                ).toBeInTheDocument();
                expect(continueButton).toHaveProperty('disabled', true);
            });
        });

        it('redirects to custom payments page after creation', async () => {
            const history = createMemoryHistory();

            mockGetBaseAccounts.mockImplementation(() =>
                Promise.resolve(customPaymentTestHelpers.baseAccountsMockResult)
            );

            (useStatementPeriodCurrentPeriod as jest.Mock).mockImplementation(
                () => customPaymentTestHelpers.currentPeriodMockResult
            );

            (useAccountDetailsForPayment as jest.Mock).mockImplementation(
                () => customPaymentTestHelpers.customPaymentAccountMockResult
            );

            (useStatementPeriodsList as jest.Mock).mockImplementation(
                () => customPaymentTestHelpers.statementPeriodsListMockResult
            );

            createCustomPayment.mockResolvedValue({
                data: {
                    createCustomPayment: {
                        worksheetPaymentCustomId: '555',
                    },
                },
            });

            renderInAppContext(
                <Router history={history}>
                    <PaymentGroupForm />
                </Router>,
                { identity: identityWithCustomPayments }
            );

            const customPaymentButton = screen.getByText(CUSTOM_PAYMENT);

            fireEvent.click(customPaymentButton);

            const continueButton = await screen.findByText('Create');

            expect(continueButton).toHaveProperty('disabled', true);

            await customPaymentTestHelpers.goThroughSelectAccountStep(
                mockGetBaseAccounts
            );

            expect(useStatementPeriodCurrentPeriod).toHaveBeenCalled();
            expect(useAccountDetailsForPayment).toHaveBeenCalledWith(
                customPaymentTestHelpers.accountDropdownOptions[0].value
            );
            expect(
                screen.queryByTestId('SkeletonLoader')
            ).not.toBeInTheDocument();

            customPaymentTestHelpers.detailsControlsLabels.forEach(label => {
                expect(screen.getByText(label)).toBeInTheDocument();
            });

            customPaymentTestHelpers.goThroughSelectCurrencyStep();

            await customPaymentTestHelpers.goThroughSelectContractStep(true);

            await customPaymentTestHelpers.fillTextField({
                label: 'amount',
                value: '100',
            });

            await customPaymentTestHelpers.fillTextField({
                label: 'wht',
                value: '20',
            });

            await customPaymentTestHelpers.fillTextField({
                label: 'wht rate',
                value: '5',
            });

            await customPaymentTestHelpers.fillTextField({
                label: 'vat',
                value: '0.1',
            });

            await customPaymentTestHelpers.fillTextField({
                label: 'vat rate',
                value: '10.0',
            });

            await customPaymentTestHelpers.goThroughSelectActivityStatementPeriod(
                true
            );

            await customPaymentTestHelpers.fillTextField({
                label: 'Payment Name',
                value: '23',
            });

            await waitFor(() => {
                expect(continueButton).toHaveProperty('disabled', false);
            });

            fireEvent.click(continueButton);

            await waitFor(() => {
                expect(history.location.pathname).toBe(
                    getCustomPaymentDetail('555')
                );
            });
        });

        it(`should call mutation with '0' values for rates and amounts when they are empty or whitespace`, async () => {
            const history = createMemoryHistory();

            mockGetBaseAccounts.mockImplementation(() =>
                Promise.resolve(customPaymentTestHelpers.baseAccountsMockResult)
            );

            (useStatementPeriodCurrentPeriod as jest.Mock).mockImplementation(
                () => customPaymentTestHelpers.currentPeriodMockResult
            );

            (useAccountDetailsForPayment as jest.Mock).mockImplementation(
                () => customPaymentTestHelpers.customPaymentAccountMockResult
            );

            (useStatementPeriodsList as jest.Mock).mockImplementation(
                () => customPaymentTestHelpers.statementPeriodsListMockResult
            );

            createCustomPayment.mockResolvedValue({
                data: {
                    createCustomPayment: {
                        worksheetPaymentCustomId: '555',
                    },
                },
            });

            renderInAppContext(
                <Router history={history}>
                    <PaymentGroupForm />
                </Router>,
                { identity: identityWithCustomPayments }
            );

            const customPaymentButton = screen.getByText(CUSTOM_PAYMENT);

            fireEvent.click(customPaymentButton);

            const continueButton = await screen.findByText('Create');

            expect(continueButton).toHaveProperty('disabled', true);

            await customPaymentTestHelpers.goThroughSelectAccountStep(
                mockGetBaseAccounts
            );

            expect(useStatementPeriodCurrentPeriod).toHaveBeenCalled();
            expect(useAccountDetailsForPayment).toHaveBeenCalledWith(
                customPaymentTestHelpers.accountDropdownOptions[0].value
            );
            expect(
                screen.queryByTestId('SkeletonLoader')
            ).not.toBeInTheDocument();

            customPaymentTestHelpers.detailsControlsLabels.forEach(label => {
                expect(screen.getByText(label)).toBeInTheDocument();
            });

            customPaymentTestHelpers.goThroughSelectCurrencyStep();

            await customPaymentTestHelpers.goThroughSelectContractStep(true);

            await customPaymentTestHelpers.fillTextField({
                label: 'amount',
                value: '100',
            });

            await customPaymentTestHelpers.fillTextField({
                label: 'vat',
                value: '   ',
            });

            await customPaymentTestHelpers.goThroughSelectActivityStatementPeriod(
                true
            );

            await customPaymentTestHelpers.fillTextField({
                label: 'Payment Name',
                value: '23',
            });

            await waitFor(() => {
                expect(continueButton).toHaveProperty('disabled', false);
            });

            fireEvent.click(continueButton);

            await waitFor(() => {
                expect(createCustomPayment).toHaveBeenCalledWith({
                    variables: {
                        input: {
                            accountId: '1',
                            activityStatementPeriodId: '265',
                            amount: '100',
                            amountAfterWithholdingAndVat: '100',
                            contractId: '102',
                            currencyCode: 'USD',
                            paymentName: '23',
                            statementPeriodId: '265',
                            vatAmount: '0',
                            vatRate: '0',
                            withholdingTaxAmount: '0',
                            withholdingTaxRate: '0',
                        },
                    },
                });
            });
        });

        it(`should call mutation with '0' for rates when amounts are explicitly set to '0' but rates are empty`, async () => {
            const history = createMemoryHistory();

            mockGetBaseAccounts.mockImplementation(() =>
                Promise.resolve(customPaymentTestHelpers.baseAccountsMockResult)
            );

            (useStatementPeriodCurrentPeriod as jest.Mock).mockImplementation(
                () => customPaymentTestHelpers.currentPeriodMockResult
            );

            (useAccountDetailsForPayment as jest.Mock).mockImplementation(
                () => customPaymentTestHelpers.customPaymentAccountMockResult
            );

            (useStatementPeriodsList as jest.Mock).mockImplementation(
                () => customPaymentTestHelpers.statementPeriodsListMockResult
            );

            createCustomPayment.mockResolvedValue({
                data: {
                    createCustomPayment: {
                        worksheetPaymentCustomId: '555',
                    },
                },
            });

            renderInAppContext(
                <Router history={history}>
                    <PaymentGroupForm />
                </Router>,
                { identity: identityWithCustomPayments }
            );

            const customPaymentButton = screen.getByText(CUSTOM_PAYMENT);

            fireEvent.click(customPaymentButton);

            const continueButton = await screen.findByText('Create');

            expect(continueButton).toHaveProperty('disabled', true);

            await customPaymentTestHelpers.goThroughSelectAccountStep(
                mockGetBaseAccounts
            );

            expect(useStatementPeriodCurrentPeriod).toHaveBeenCalled();
            expect(useAccountDetailsForPayment).toHaveBeenCalledWith(
                customPaymentTestHelpers.accountDropdownOptions[0].value
            );
            expect(
                screen.queryByTestId('SkeletonLoader')
            ).not.toBeInTheDocument();

            customPaymentTestHelpers.detailsControlsLabels.forEach(label => {
                expect(screen.getByText(label)).toBeInTheDocument();
            });

            customPaymentTestHelpers.goThroughSelectCurrencyStep();

            await customPaymentTestHelpers.goThroughSelectContractStep(true);

            await customPaymentTestHelpers.fillTextField({
                label: 'amount',
                value: '100',
            });

            await customPaymentTestHelpers.fillTextField({
                label: 'wht',
                value: '0',
            });

            await customPaymentTestHelpers.fillTextField({
                label: 'vat',
                value: '0',
            });

            await customPaymentTestHelpers.goThroughSelectActivityStatementPeriod(
                true
            );

            await customPaymentTestHelpers.fillTextField({
                label: 'Payment Name',
                value: '23',
            });

            await waitFor(() => {
                expect(continueButton).toHaveProperty('disabled', false);
            });

            fireEvent.click(continueButton);

            await waitFor(() => {
                expect(createCustomPayment).toHaveBeenCalledWith({
                    variables: {
                        input: {
                            accountId: '1',
                            activityStatementPeriodId: '265',
                            amount: '100',
                            amountAfterWithholdingAndVat: '100',
                            contractId: '102',
                            currencyCode: 'USD',
                            paymentName: '23',
                            statementPeriodId: '265',
                            vatAmount: '0',
                            vatRate: '0',
                            withholdingTaxAmount: '0',
                            withholdingTaxRate: '0',
                        },
                    },
                });
            });
        });
        it(`should call mutation with '0' for amounts when rates are explicitly set to '0' but amounts are empty`, async () => {
            const history = createMemoryHistory();

            mockGetBaseAccounts.mockImplementation(() =>
                Promise.resolve(customPaymentTestHelpers.baseAccountsMockResult)
            );

            (useStatementPeriodCurrentPeriod as jest.Mock).mockImplementation(
                () => customPaymentTestHelpers.currentPeriodMockResult
            );

            (useAccountDetailsForPayment as jest.Mock).mockImplementation(
                () => customPaymentTestHelpers.customPaymentAccountMockResult
            );

            (useStatementPeriodsList as jest.Mock).mockImplementation(
                () => customPaymentTestHelpers.statementPeriodsListMockResult
            );

            createCustomPayment.mockResolvedValue({
                data: {
                    createCustomPayment: {
                        worksheetPaymentCustomId: '555',
                    },
                },
            });

            renderInAppContext(
                <Router history={history}>
                    <PaymentGroupForm />
                </Router>,
                { identity: identityWithCustomPayments }
            );

            const customPaymentButton = screen.getByText(CUSTOM_PAYMENT);

            fireEvent.click(customPaymentButton);

            const continueButton = await screen.findByText('Create');

            expect(continueButton).toHaveProperty('disabled', true);

            await customPaymentTestHelpers.goThroughSelectAccountStep(
                mockGetBaseAccounts
            );

            expect(useStatementPeriodCurrentPeriod).toHaveBeenCalled();
            expect(useAccountDetailsForPayment).toHaveBeenCalledWith(
                customPaymentTestHelpers.accountDropdownOptions[0].value
            );
            expect(
                screen.queryByTestId('SkeletonLoader')
            ).not.toBeInTheDocument();

            customPaymentTestHelpers.detailsControlsLabels.forEach(label => {
                expect(screen.getByText(label)).toBeInTheDocument();
            });

            customPaymentTestHelpers.goThroughSelectCurrencyStep();

            await customPaymentTestHelpers.goThroughSelectContractStep(true);

            await customPaymentTestHelpers.fillTextField({
                label: 'amount',
                value: '100',
            });

            await customPaymentTestHelpers.fillTextField({
                label: 'wht rate',
                value: '0',
            });

            await customPaymentTestHelpers.fillTextField({
                label: 'vat rate',
                value: '0',
            });

            await customPaymentTestHelpers.goThroughSelectActivityStatementPeriod(
                true
            );

            await customPaymentTestHelpers.fillTextField({
                label: 'Payment Name',
                value: '23',
            });

            await waitFor(() => {
                expect(continueButton).toHaveProperty('disabled', false);
            });

            fireEvent.click(continueButton);

            await waitFor(() => {
                expect(createCustomPayment).toHaveBeenCalledWith({
                    variables: {
                        input: {
                            accountId: '1',
                            activityStatementPeriodId: '265',
                            amount: '100',
                            amountAfterWithholdingAndVat: '100',
                            contractId: '102',
                            currencyCode: 'USD',
                            paymentName: '23',
                            statementPeriodId: '265',
                            vatAmount: '0',
                            vatRate: '0',
                            withholdingTaxAmount: '0',
                            withholdingTaxRate: '0',
                        },
                    },
                });
            });
        });
    });

    it('redirects to the payments when cancel button is clicked', () => {
        render();

        const cancelButton = screen.getByText('Cancel');
        const cancelLink = cancelButton.closest('a');

        expect(cancelLink).not.toBeNull();
        expect(cancelLink).toHaveAttribute(
            'href',
            expect.stringContaining('/payments')
        );
    });

    it('disables the continue button when input is a blank string', async () => {
        render();

        const groupButton = screen.getByText(EXISTING_GROUP);
        fireEvent.click(groupButton);

        const continueButton = await screen.findByText('Create');

        fireEvent.change(screen.getByTestId('input-payment-name'), {
            target: { value: '     ' },
        });

        expect(continueButton).toHaveProperty('disabled');
    });

    it('enables the continue button when input is a valid string', async () => {
        render();

        const groupButton = screen.getByText(EXISTING_GROUP);
        fireEvent.click(groupButton);

        const continueButton = await screen.findByText('Create');
        const groupNameSelect = screen.getByRole('combobox');

        fireEvent.change(screen.getByTestId('input-payment-name'), {
            target: { value: 'Test Payment' },
        });

        fireEvent.change(groupNameSelect, {
            target: { value: 'Test Group Name' },
        });
        fireEvent.keyDown(groupNameSelect, {
            key: 'Enter',
            keyCode: 13,
            which: 13,
        });

        await waitFor(() => {
            expect(continueButton).toHaveProperty('disabled', false);
        });
    });

    describe('Submit button text', () => {
        const identityWithDraftPayments = createIdentity({
            features: { [USER_FEATURES.ABACUS_TAP_DRAFT_PAYMENTS]: true },
        });
        const identityWithoutDraftPayments = createIdentity({
            features: { [USER_FEATURES.ABACUS_TAP_DRAFT_PAYMENTS]: false },
        });

        it('shows "Create" when isTapDraftPaymentsEnabled is false for EXISTING_GROUP', () => {
            render(identityWithoutDraftPayments);

            fireEvent.click(screen.getByText(EXISTING_GROUP));

            expect(screen.getByText('Create')).toBeInTheDocument();
            expect(screen.queryByText('Save As Draft')).not.toBeInTheDocument();
        });

        it('shows "Save As Draft" when isTapDraftPaymentsEnabled is true and method is EXISTING_GROUP', () => {
            render(identityWithDraftPayments);

            fireEvent.click(screen.getByText(EXISTING_GROUP));

            expect(screen.getByText('Save As Draft')).toBeInTheDocument();
            expect(screen.queryByText('Create')).not.toBeInTheDocument();
        });

        it('shows "Save As Draft" when isTapDraftPaymentsEnabled is true and method is SPECIFIC_GROUP', () => {
            render(identityWithDraftPayments);

            fireEvent.click(screen.getByText(SPECIFIC_GROUP));

            expect(screen.getByText('Save As Draft')).toBeInTheDocument();
            expect(screen.queryByText('Create')).not.toBeInTheDocument();
        });

        it('shows "Create" when isTapDraftPaymentsEnabled is true but method is SPECIFIC_ADVANCE', () => {
            render(identityWithDraftPayments);

            fireEvent.click(screen.getByText(SPECIFIC_ADVANCE));

            expect(screen.getByText('Create')).toBeInTheDocument();
            expect(screen.queryByText('Save As Draft')).not.toBeInTheDocument();
        });

        it('shows "Create" when isTapDraftPaymentsEnabled is true but method is CUSTOM_PAYMENT', () => {
            render(identityWithDraftPayments);

            fireEvent.click(screen.getByText(CUSTOM_PAYMENT));

            expect(screen.getByText('Create')).toBeInTheDocument();
            expect(screen.queryByText('Save As Draft')).not.toBeInTheDocument();
        });

        it('shows "Create" when isTapDraftPaymentsEnabled is false for SPECIFIC_GROUP', () => {
            render(identityWithoutDraftPayments);

            fireEvent.click(screen.getByText(SPECIFIC_GROUP));

            expect(screen.getByText('Create')).toBeInTheDocument();
            expect(screen.queryByText('Save As Draft')).not.toBeInTheDocument();
        });
    });
});
