import React from 'react';
import { ApolloError } from '@apollo/client/errors';
import { fireEvent, screen } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import * as createPaymentHoldResponse from 'src/__fixtures__/graphql/create-payment-hold-response.json';
import * as paymentHoldResponse from 'src/__fixtures__/graphql/payment-hold-response.json';
import * as paymentHoldMutation from 'src/apollo/mutations/payment-hold';
import * as identityQuery from 'src/apollo/queries/identity';
import * as paymentHoldQuery from 'src/apollo/queries/payment-hold';
import PaymentHoldForm from 'src/components/payment-hold/payment-hold-form';
import { HOLD_STATUSES } from 'src/constants';
import * as validations from 'src/utils/form-validations';

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

describe('<PaymentHoldForm />', () => {
    const { ACTIVE, ON_HOLD } = HOLD_STATUSES;
    const createHoldSpy = jest
        .fn()
        .mockResolvedValue({ data: createPaymentHoldResponse });
    let validationSpy: jest.SpyInstance;

    afterEach(jest.restoreAllMocks);

    beforeEach(() => {
        jest.spyOn(paymentHoldQuery, 'usePaymentHold').mockReturnValue({
            data: paymentHoldResponse,
            error: new ApolloError({}),
            loading: false,
            refetch: jest.fn(),
        });
        jest.spyOn(paymentHoldMutation, 'useCreatePaymentHold').mockReturnValue(
            createHoldSpy
        );
        validationSpy = jest.spyOn(validations, 'paymentHoldFormValidation');
    });

    it('renders buttons for each hold status', () => {
        render();
        expect(screen.getAllByText(ACTIVE).length).toBeGreaterThan(0);
        expect(screen.getAllByText(ON_HOLD).length).toBeGreaterThan(0);
    });

    it('does not display the form on render', () => {
        render();
        expect(screen.queryByTestId('start-date')).toBeNull();
    });

    it('displays the form when there is NO hold and ON HOLD is clicked', () => {
        render();

        const onHoldBtn = screen.getAllByText(ON_HOLD)[0];
        fireEvent.click(onHoldBtn);

        expect(onHoldBtn.classList).toContain('active');
        expect(screen.getByTestId('start-date').firstChild).toHaveClass(
            'SuiteDatePicker'
        );
        expect(screen.getByTestId('input-reason')).toBeDefined();
        expect(screen.getAllByText('Cancel').length).toBeGreaterThan(0);
        expect(screen.getAllByText('Place Hold').length).toBeGreaterThan(0);
    });

    it('displays the form when there IS a hold and ACTIVE is clicked', () => {
        jest.spyOn(paymentHoldQuery, 'usePaymentHold').mockReturnValue({
            data: {
                abacusPaymentHold: {
                    ...paymentHoldResponse.abacusPaymentHold,
                    isOnHold: true,
                },
            },
            error: new ApolloError({}),
            loading: false,
            refetch: jest.fn(),
        });
        render();

        const activeBtn = screen.getAllByText(ACTIVE)[0];
        fireEvent.click(activeBtn);
        expect(activeBtn.classList).toContain('active');
        expect(screen.getByTestId('start-date').firstChild).toHaveClass(
            'SuiteDatePicker'
        );
        expect(screen.getByTestId('input-reason')).toBeDefined();
        expect(screen.getAllByText('Cancel').length).toBeGreaterThan(0);
        expect(screen.getAllByText('Remove Hold').length).toBeGreaterThan(0);
    });

    it('displays validation errors when present', () => {
        validationSpy.mockReturnValue({ reason: 'Cannot be blank' });
        render();

        const onHoldBtn = screen.getAllByText(ON_HOLD)[0];
        fireEvent.click(onHoldBtn);

        const saveBtn = screen.getAllByText('Place Hold')[0];
        fireEvent.click(saveBtn);

        expect(createHoldSpy).not.toHaveBeenCalled();
        expect(validationSpy).toHaveBeenCalled();
        expect(screen.getAllByText('Cannot be blank').length).toBeGreaterThan(
            0
        );
    });

    it('sends a POST request when PLACE HOLD or REMOVE HOLD is clicked', () => {
        validationSpy.mockReturnValue({});
        render();

        const onHoldBtn = screen.getAllByText(ON_HOLD)[0];
        fireEvent.click(onHoldBtn);

        const saveBtn = screen.getAllByText('Place Hold')[0];
        fireEvent.click(saveBtn);

        expect(createHoldSpy).toHaveBeenCalled();
    });

    it('renders the user who created each payment hold', async () => {
        const mockIdentityResponse = {
            identityById: null,
        };
        jest.spyOn(
            identityQuery,
            'useGetIdentityByIdLazyQuery'
        ).mockImplementation(() => ({
            data: mockIdentityResponse,
            loading: false,
            getIdentityById: jest.fn().mockResolvedValue({
                data: mockIdentityResponse,
            }),
        }));

        render();

        for (const item of paymentHoldResponse.abacusPaymentHold
            .paymentHoldHistory)
            expect(
                await screen.findAllByText(item.lastModifiedBy)
            ).toBeDefined();
    });

    it('hides table when paymentHoldHistory value is null', () => {
        jest.spyOn(paymentHoldQuery, 'usePaymentHold').mockReturnValue({
            data: {
                abacusPaymentHold: {
                    ...paymentHoldResponse.abacusPaymentHold,
                    paymentHoldHistory: null,
                },
            },
            error: new ApolloError({}),
            loading: false,
            refetch: jest.fn(),
        });

        render();

        expect(
            screen.getByTestId('paymentHoldHistoryContentTestid')
        ).toHaveTextContent('');
    });
});
