import React from 'react';
import { fireEvent, screen, waitFor } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import * as abacusStateMutation from 'src/apollo/mutations/abacus-state';
import * as accountPayeeMutation from 'src/apollo/mutations/account-payee';
import TaxFormInfoSubsection, {
    REQUEST_NEW_FORM_BTN_TEXT,
    REQUEST_NEW_FORM_TOAST_MSG,
    TaxFormInfoSubsectionPropTypes,
} from '../tax-form-info-subsection';
import { useCanPerformAction } from 'src/apollo/queries/can-perform';
import { ABACUS_ACTION_STATUSES } from '@theorchard/accounting-apps-shared';

jest.mock('src/apollo/queries/can-perform', () => ({
    useCanPerformAction: jest.fn(),
}));

describe('<TaxFormInfoSubsection>', () => {
    const defaultProps: TaxFormInfoSubsectionPropTypes = {
        accountId: '32145',
        accountPayeeId: '123456',
        isTaxFormInfoEmpty: false,
        subsection: {
            title: 'Title',
            subtitle: 'Subtitle',
            variant: 'success',
            notes: 'Notes message',
        },
    };

    afterEach(jest.restoreAllMocks);

    const render = (props: TaxFormInfoSubsectionPropTypes) =>
        renderInAppContext(<TaxFormInfoSubsection {...props} />);

    it('renders', () => {
        (useCanPerformAction as jest.Mock).mockReturnValue({
            data: true,
            loading: false,
        });

        const { container } = render(defaultProps);

        expect(container).toBeDefined();
        expect(screen.getByText(defaultProps.subsection.title)).toBeDefined();
        expect(
            screen.getByText(defaultProps.subsection.subtitle)
        ).toBeDefined();
        expect(screen.getByText('See notes')).toBeDefined();
        expect(screen.getByText('Notes message')).toBeDefined();
    });

    it('renders loading skeleton when canPerformAction query is loading', () => {
        (useCanPerformAction as jest.Mock).mockReturnValue({
            data: undefined,
            loading: true,
        });

        render(defaultProps);

        expect(screen.getByTestId('SkeletonLoader')).toBeInTheDocument();
    });

    test('does not render action button when user cannot perform action', () => {
        (useCanPerformAction as jest.Mock).mockReturnValue({
            data: false,
            loading: false,
        });

        render(defaultProps);

        expect(
            screen.queryByText(REQUEST_NEW_FORM_BTN_TEXT)
        ).not.toBeInTheDocument();
    });

    test('does not render action button for ES account', () => {
        (useCanPerformAction as jest.Mock).mockReturnValue({
            data: false,
            loading: false,
        });

        render({ ...defaultProps, isNonSpanishTaxResident: true });

        expect(
            screen.queryByText(REQUEST_NEW_FORM_BTN_TEXT)
        ).not.toBeInTheDocument();
    });

    describe(`when ${REQUEST_NEW_FORM_BTN_TEXT} button is clicked`, () => {
        const mockUpdateAbacusState = jest.fn().mockResolvedValue({
            data: {
                abacusUpdateState: {
                    abacusStateId: '293846',
                    actionName: 'tax_eligibility',
                    actionStatus: ABACUS_ACTION_STATUSES.RUNNING,
                    __typename: 'AbacusState',
                },
            },
        });

        const mockDeleteTaxFormInfo = jest.fn().mockResolvedValue({
            data: {
                deleteAccountPayeeTaxFormInfo: {
                    success: true,
                },
            },
        });

        beforeEach(() => {
            (useCanPerformAction as jest.Mock).mockReturnValue({
                data: true,
                loading: false,
            });

            jest.spyOn(
                abacusStateMutation,
                'useUpdateAbacusState'
            ).mockReturnValue({
                updateAbacusState: mockUpdateAbacusState,
                loading: false,
            });

            jest.spyOn(
                accountPayeeMutation,
                'useDeleteAccountPayeeTaxFormInfo'
            ).mockReturnValue({
                deleteTaxFormInfo: mockDeleteTaxFormInfo,
                loading: false,
            });
        });

        it('displays loading skeleton when mutations are in loading state', async () => {
            jest.spyOn(
                abacusStateMutation,
                'useUpdateAbacusState'
            ).mockReturnValue({
                updateAbacusState: mockUpdateAbacusState,
                loading: true,
            });

            jest.spyOn(
                accountPayeeMutation,
                'useDeleteAccountPayeeTaxFormInfo'
            ).mockReturnValue({
                deleteTaxFormInfo: mockDeleteTaxFormInfo,
                loading: true,
            });

            render({
                ...defaultProps,
                accountPayeeId: '3333',
                isTaxFormInfoEmpty: false,
            });

            expect(screen.getByTestId('SkeletonLoader')).toBeInTheDocument();
        });

        it('calls deleteTaxFormInfo if tax form info is not empty and accountPayeeId exists', async () => {
            render({
                ...defaultProps,
                accountPayeeId: '3333',
                isTaxFormInfoEmpty: false,
            });

            const actionButton = screen.getByText(REQUEST_NEW_FORM_BTN_TEXT);
            fireEvent.click(actionButton);

            await waitFor(() => {
                expect(mockDeleteTaxFormInfo).toHaveBeenCalledWith({
                    variables: { accountPayeeId: '3333' },
                });
            });

            const toast = await screen.findByTestId('Toast-0');
            expect(toast).toHaveTextContent(REQUEST_NEW_FORM_TOAST_MSG.SUCESS);
        });

        it('calls updateAbacusState if tax form info is empty and taxEligibilityStateId exists', async () => {
            render({
                ...defaultProps,
                isTaxFormInfoEmpty: true,
                taxEligibilityStateId: '3333',
            });

            const actionButton = screen.getByText(REQUEST_NEW_FORM_BTN_TEXT);
            fireEvent.click(actionButton);

            await waitFor(() => {
                expect(mockUpdateAbacusState).toHaveBeenCalledWith({
                    variables: {
                        abacusStateId: '3333',
                        actionStatus: ABACUS_ACTION_STATUSES.RUNNING,
                    },
                });
            });
            const toast = await screen.findByTestId('Toast-0');
            expect(toast).toHaveTextContent(REQUEST_NEW_FORM_TOAST_MSG.SUCESS);
        });
    });

    describe('when an error occurs', () => {
        const mockUpdateAbacusState = jest
            .fn()
            .mockRejectedValue([{ message: 'Failed to update state' }]);

        const mockDeleteTaxFormInfo = jest.fn().mockRejectedValue({
            message: 'Failed to delete tax form info',
        });

        beforeEach(() => {
            jest.spyOn(
                accountPayeeMutation,
                'useDeleteAccountPayeeTaxFormInfo'
            ).mockReturnValue({
                deleteTaxFormInfo: mockDeleteTaxFormInfo,
                loading: false,
            });

            jest.spyOn(
                abacusStateMutation,
                'useUpdateAbacusState'
            ).mockReturnValue({
                updateAbacusState: mockUpdateAbacusState,
                loading: false,
            });
        });

        it('displays an error alert when deleteTaxFormInfo fails', async () => {
            render({
                ...defaultProps,
                accountPayeeId: '132',
                isTaxFormInfoEmpty: false,
            });

            const actionButton = screen.getByText(REQUEST_NEW_FORM_BTN_TEXT);
            fireEvent.click(actionButton);

            await waitFor(() => {
                expect(
                    screen.getByText('Failed to delete tax form info')
                ).toBeDefined();
            });
            const toast = screen.getByTestId('Toast-0');
            expect(toast).toHaveTextContent(REQUEST_NEW_FORM_TOAST_MSG.ERROR);
        });

        it('displays an error alert when updateAbacusState fails', async () => {
            render({
                ...defaultProps,
                isTaxFormInfoEmpty: true,
                taxEligibilityStateId: '3333',
            });

            const actionButton = screen.getByText(REQUEST_NEW_FORM_BTN_TEXT);
            fireEvent.click(actionButton);

            await waitFor(() => {
                expect(
                    screen.getByText('Failed to update state')
                ).toBeDefined();
            });
            const toast = screen.getByTestId('Toast-0');
            expect(toast).toHaveTextContent(REQUEST_NEW_FORM_TOAST_MSG.ERROR);
        });
    });
});
