import React from 'react';
import * as apolloClient from '@apollo/client';
import { fireEvent, screen, waitFor, within } from '@testing-library/react';
import * as suiteComponents from '@theorchard/suite-components';
import { renderInAppContext } from '@theorchard/suite-testing';
import { referenceSigningEntities } from 'src/__fixtures__/graphql/reference-signing-entity';
import {
    EMPTY_MAPPED_SIGNING_ENTITIES_DATA,
    MAPPED_SIGNING_ENTITIES_DATA,
} from 'src/__fixtures__/graphql/signing-entity-sap-profit-center';
import * as profitCenterMutations from 'src/apollo/mutations/reference-sap-profit-center';
import * as profitCenterQueries from 'src/apollo/queries/reference-sap-profit-center';
import * as signingEntitiesQuery from 'src/apollo/queries/reference-signing-entity';
import { SIGNING_ENTITIES_NOT_MAPPED } from 'src/apollo/type-constants/reference-sap-profit-center';
import {
    EditMappedSigningEntitiesFormFields,
    EditMappedSigningEntitiesFormFieldsPropTypes,
} from 'src/components/reference-sap-profit-center-edit/edit-mapped-signing-entities-form-fields';
import * as sidecarActivityContext from 'src/components/reference-sap-profit-center-edit/sidecar-activity-context';

describe('<EditMappedSigningEntitiesFormFields />', () => {
    afterEach(jest.restoreAllMocks);

    const defaultProps: EditMappedSigningEntitiesFormFieldsPropTypes = {
        changeHandler: jest.fn(),
        error: null,
        formData: {
            displayName: 'PC US9682',
            referenceSapProfitCenterId: '18',
            referenceSigningEntityIds: [],
        },
        submitHandler: jest.fn(),
    };

    let requestSpy: jest.SpyInstance;
    let setDeleteError: jest.Mock;

    beforeEach(() => {
        requestSpy = jest
            .spyOn(profitCenterQueries, 'useSigningEntitiesByProfitCenter')
            .mockReturnValue({
                data: MAPPED_SIGNING_ENTITIES_DATA,
                error: undefined,
                loading: false,
                refetch: jest.fn(),
            });
        jest.spyOn(
            signingEntitiesQuery,
            'useReferenceSigningEntities'
        ).mockReturnValue({
            data: referenceSigningEntities,
            loading: false,
            error: undefined,
        });
        setDeleteError = jest.fn();
        jest.spyOn(
            sidecarActivityContext,
            'useSidecarActivity'
        ).mockReturnValue({
            isDeletingEntity: false,
            isSavingFormData: false,
            deleteError: null,
            setDeleteError,
            setIsDeletingEntity: jest.fn(),
        });
        jest.spyOn(
            profitCenterMutations,
            'useDeleteMappedSigningEntity'
        ).mockReturnValue(jest.fn().mockResolvedValue(undefined));
        jest.spyOn(suiteComponents, 'useToast').mockReturnValue(jest.fn());
        jest.spyOn(apolloClient, 'useApolloClient').mockReturnValue({
            cache: { evict: jest.fn() },
        } as unknown as apolloClient.ApolloClient<object>);
    });

    const renderComponent = (
        props: EditMappedSigningEntitiesFormFieldsPropTypes = defaultProps
    ) => renderInAppContext(<EditMappedSigningEntitiesFormFields {...props} />);

    it('requests signing entities mapped to the given profit center on render', () => {
        renderComponent();

        expect(requestSpy).toHaveBeenCalledWith({
            limit: 5,
            offset: 0,
            referenceSapProfitCenterId: '18',
        });
    });

    it('renders the signing entity dropdown and the Add button', () => {
        renderComponent();

        expect(screen.getByText('Select Signing Entity')).toBeDefined();
        expect(screen.getByText('Add')).toBeDefined();
    });

    it('renders the list of currently mapped signing entities', () => {
        renderComponent();

        expect(screen.getByText('AWAL Digital Limited')).toBeDefined();
        expect(
            screen.getByText('Kollective Neighbouring Rights Limited')
        ).toBeDefined();
    });

    it('shows the empty state when no signing entities are mapped', () => {
        requestSpy.mockReturnValue({
            data: EMPTY_MAPPED_SIGNING_ENTITIES_DATA,
            error: undefined,
            loading: false,
            refetch: jest.fn(),
        });
        renderComponent();

        expect(screen.getByText(SIGNING_ENTITIES_NOT_MAPPED)).toBeDefined();
    });

    it('shows the list error message when the query fails', () => {
        const listErrorMessage = 'Failed to load mapped signing entities.';
        requestSpy.mockReturnValue({
            data: EMPTY_MAPPED_SIGNING_ENTITIES_DATA,
            error: { message: listErrorMessage } as apolloClient.ApolloError,
            loading: false,
            refetch: jest.fn(),
        });
        renderComponent();

        const alertElement = screen.getByText(listErrorMessage);
        expect(alertElement).toBeInTheDocument();
        expect(alertElement.closest('.errorMsg')).toBeInTheDocument();
    });

    it('calls changeHandler with the selected signing entity ids', async () => {
        renderComponent();

        const selectButton = within(
            screen.getByTestId('signingEntityMultiSelect')
        ).getByRole('button');
        fireEvent.click(selectButton);

        await screen.findByText('AWAL Recordings America, Inc.');
        fireEvent.click(screen.getByText('AWAL Recordings America, Inc.'));

        expect(defaultProps.changeHandler).toHaveBeenCalledWith(
            'referenceSigningEntityIds',
            ['4']
        );
    });

    it('disables the Add button when no entities are selected', () => {
        renderComponent();

        const addButton = screen.getByText('Add');
        expect(addButton).toBeDisabled();
    });

    it('disables the Add button and shows "Adding..." while the form is saving', () => {
        jest.spyOn(
            sidecarActivityContext,
            'useSidecarActivity'
        ).mockReturnValue({
            isDeletingEntity: false,
            isSavingFormData: true,
            deleteError: null,
            setDeleteError,
            setIsDeletingEntity: jest.fn(),
        });
        renderComponent();

        const addButton = screen.getByText('Adding...');
        expect(addButton).toBeDisabled();
    });

    it('shows the error prop message in the alert', () => {
        renderComponent({
            ...defaultProps,
            error: 'The selected signing entities may already be mapped.',
        });

        expect(
            screen.getByText(
                'The selected signing entities may already be mapped.'
            )
        ).toBeInTheDocument();
    });

    it('steps back to the previous page after deleting the last row on the current page', async () => {
        const singleItemOnLastPage: GetSigningEntitiesByProfitCenterQuery = {
            abacusSigningEntitiesBySapProfitCenter: {
                totalCount: 6,
                items: [
                    {
                        signingEntitySapProfitCenterId: 'sep-6',
                        signingEntity: {
                            referenceSigningEntityId: '9',
                            legalName: 'Kollective Neighbouring Rights Limited',
                            companyCode: '4918',
                        },
                    },
                ],
            },
        };
        requestSpy.mockReturnValue({
            data: singleItemOnLastPage,
            error: undefined,
            loading: false,
            refetch: jest.fn(),
        });
        renderComponent();

        fireEvent.click(screen.getByTestId('SuitePagination-arrow-next'));

        expect(requestSpy).toHaveBeenLastCalledWith({
            limit: 5,
            offset: 5,
            referenceSapProfitCenterId: '18',
        });

        fireEvent.click(screen.getByTestId('TrashGlyphIcon'));
        fireEvent.click(screen.getByText('Confirm'));

        await waitFor(() =>
            expect(requestSpy).toHaveBeenLastCalledWith({
                limit: 5,
                offset: 0,
                referenceSapProfitCenterId: '18',
            })
        );
    });

    it('shows the deleteError from the sidecar activity context in the alert', () => {
        jest.spyOn(
            sidecarActivityContext,
            'useSidecarActivity'
        ).mockReturnValue({
            isDeletingEntity: false,
            isSavingFormData: false,
            deleteError: 'Failed to delete mapped signing entity.',
            setDeleteError,
            setIsDeletingEntity: jest.fn(),
        });
        renderComponent();

        expect(
            screen.getByText('Failed to delete mapped signing entity.')
        ).toBeInTheDocument();
    });
});
