import React from 'react';
import { fireEvent, screen } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import { contractDetails as contractResponse } from 'src/__fixtures__/graphql/contract-details';
import * as mutations from 'src/apollo/mutations/contract-reserve';
import * as contractQuery from 'src/apollo/queries/contract';
import { CONTRACT_TYPES } from 'src/constants';
import * as formValidation from 'src/utils/form-validations/physical-reserve-form-validation';
import { PhysicalReserveForm } from '../physical-reserve-form';
import type { GetContractQuery } from 'src/apollo/queries/contract/__generated__/get-contract';

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

describe('<PhysicalReserveForm>', () => {
    const { abacusContract } = contractResponse;
    const mockReserve = contractResponse.abacusContract.reserve;
    let useContractSpy: any;

    const addContractReserve = jest.fn().mockResolvedValue({
        data: { abacusCreateContractReserve: mockReserve },
        loading: false,
    });
    const updateContractReserve = jest.fn().mockResolvedValue({
        data: { abacusUpdateContractReserve: mockReserve },
        loading: false,
    });

    beforeEach(() => {
        useContractSpy = jest
            .spyOn(contractQuery, 'useContract')
            .mockReturnValue({
                data: { abacusContract } as GetContractQuery,
                error: undefined,
                loading: false,
            });
        jest.spyOn(mutations, 'useCreateContractReserve').mockReturnValue(
            addContractReserve
        );
        jest.spyOn(mutations, 'useUpdateContractReserve').mockReturnValue(
            updateContractReserve
        );
    });

    const render = () => renderInAppContext(<PhysicalReserveForm />);

    it('renders', () => {
        render();
        expect(
            screen.getByText('Physical Reserves', {
                ignore: '.breadcrumb-item',
            })
        ).toBeDefined();
        expect(screen.getByTestId('reserveRateFieldTestId')).toBeDefined();
        expect(
            screen.getByTestId('installmentsInMonthsFieldTestId')
        ).toBeDefined();
        expect(screen.getByTestId('reserveReleaseFieldTestId')).toBeDefined();
    });

    it('requests contract detail on render', () => {
        render();
        expect(useContractSpy).toHaveBeenCalledWith(1);
    });

    it('redirects to contract detail when cancel button is clicked', () => {
        render();
        const cancelBtn: any = screen.getByRole('button', { name: 'Cancel' });
        expect(cancelBtn?.parentElement?.href).toContain('/contract/1');
    });

    describe('Save Button', () => {
        it('displays error message when clicked if form validation fails', () => {
            const validationSpy = jest
                .spyOn<
                    any,
                    any
                >(formValidation, 'physicalReserveFormValidation')
                .mockReturnValue({
                    reserveRate: 'Reserve Rate Field must not be blank',
                });

            render();

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

            expect(validationSpy).toHaveBeenCalled();
            expect(addContractReserve).not.toHaveBeenCalled();
            expect(
                screen.getByText('Reserve Rate Field must not be blank')
            ).toBeDefined();
        });

        it('does not update contract reserve if a reserve already exists but values have not changed', () => {
            const validationSpy = jest
                .spyOn<
                    any,
                    any
                >(formValidation, 'physicalReserveFormValidation')
                .mockReturnValue({});

            render();

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

            expect(validationSpy).toHaveBeenCalled();
            expect(updateContractReserve).not.toHaveBeenCalled();
        });

        it('updates contract reserve if a reserve already exists and values have changed', () => {
            const validationSpy = jest
                .spyOn<
                    any,
                    any
                >(formValidation, 'physicalReserveFormValidation')
                .mockReturnValue({});

            render();

            const reserveRate = screen
                .getByTestId('reserveRateFieldTestId')
                .querySelector('input');
            if (reserveRate)
                fireEvent.change(reserveRate, { target: { value: '25.00' } });

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

            expect(validationSpy).toHaveBeenCalled();
            expect(updateContractReserve).toHaveBeenCalled();
        });

        it('creates a contract reserve if contract does not have a reserve', () => {
            const contractWithoutReserve = {
                ...abacusContract,
                reserve: null,
            };

            useContractSpy = jest
                .spyOn(contractQuery, 'useContract')
                .mockReturnValue({
                    data: {
                        abacusContract: contractWithoutReserve,
                    } as GetContractQuery,
                    error: undefined,
                    loading: false,
                });

            const validationSpy = jest
                .spyOn<
                    any,
                    any
                >(formValidation, 'physicalReserveFormValidation')
                .mockReturnValue({});

            render();

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

            expect(validationSpy).toHaveBeenCalled();
            expect(addContractReserve).toHaveBeenCalled();
        });
    });

    it("doesn't render physical reserves form for neighbouring_rights contract", () => {
        jest.spyOn(contractQuery, 'useContract').mockReturnValue({
            data: {
                abacusContract: {
                    ...abacusContract,
                    contractType: CONTRACT_TYPES.NEIGHBOURING_RIGHTS,
                } as GetContractQuery['abacusContract'],
            },
            error: undefined,
            loading: false,
        });

        render();
        expect(
            screen.getByText(
                'Physical Reserves can not be added to "Performer Neighbouring Rights" contract.'
            )
        ).toBeDefined();
        expect(screen.queryByText('Type')).toBeNull();
    });
});
