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 } 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 { FAILED_TO_MAPPED_SIGNING_ENTITIES } from 'src/apollo/type-constants/reference-sap-profit-center';
import {
    EditMappedSigningEntities,
    EditMappedSigningEntitiesPropTypes,
} from 'src/components/reference-sap-profit-center-edit/edit-mapped-signing-entities';

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

    let addToastMock: jest.Mock;
    let mockEvict: jest.Mock;
    let bulkCreateSigningEntities: jest.Mock;

    const defaultProps: EditMappedSigningEntitiesPropTypes = {
        isSidecarOpen: true,
        profitCenter: {
            displayName: 'PC US9682',
            referenceSapProfitCenterId: '18',
        },
        setIsSidecarOpen: jest.fn(),
    };

    beforeEach(() => {
        addToastMock = jest.fn();
        mockEvict = jest.fn();
        bulkCreateSigningEntities = jest.fn().mockResolvedValue(undefined);

        jest.spyOn(
            profitCenterQueries,
            'useSigningEntitiesByProfitCenter'
        ).mockReturnValue({
            data: EMPTY_MAPPED_SIGNING_ENTITIES_DATA,
            error: undefined,
            loading: false,
            refetch: jest.fn(),
        });
        jest.spyOn(
            signingEntitiesQuery,
            'useReferenceSigningEntities'
        ).mockReturnValue({
            data: referenceSigningEntities,
            loading: false,
            error: undefined,
        });
        jest.spyOn(
            profitCenterMutations,
            'useBulkCreateSigningEntitySapProfitCenters'
        ).mockReturnValue(bulkCreateSigningEntities);
        jest.spyOn(
            profitCenterMutations,
            'useDeleteMappedSigningEntity'
        ).mockReturnValue(jest.fn().mockResolvedValue(undefined));
        jest.spyOn(suiteComponents, 'useToast').mockReturnValue(addToastMock);
        jest.spyOn(apolloClient, 'useApolloClient').mockReturnValue({
            cache: { evict: mockEvict },
        } as unknown as apolloClient.ApolloClient<object>);
    });

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

    const selectSigningEntity = (legalName: string) => {
        const selectButton = within(
            screen.getByTestId('signingEntityMultiSelect')
        ).getByRole('button');
        fireEvent.click(selectButton);
        fireEvent.click(screen.getByText(legalName));
    };

    it('renders the sidecar with the profit center display name as the title when open', () => {
        renderComponent();

        expect(screen.getByTestId('ProfitCenterFormSidecar')).toBeDefined();
        expect(screen.getByText('PC US9682')).toBeDefined();
    });

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

        expect(
            profitCenterQueries.useSigningEntitiesByProfitCenter
        ).toHaveBeenCalledWith(
            expect.objectContaining({ referenceSapProfitCenterId: '18' })
        );
    });

    it('submits the selected signing entities, shows a success toast, and evicts cached lists', async () => {
        renderComponent();

        selectSigningEntity('AWAL Digital Limited');
        fireEvent.click(screen.getByText('Add'));

        await waitFor(() =>
            expect(bulkCreateSigningEntities).toHaveBeenCalledWith({
                variables: {
                    referenceSapProfitCenterId: '18',
                    referenceSigningEntityIds: ['1'],
                },
            })
        );

        await waitFor(() =>
            expect(addToastMock).toHaveBeenCalledWith(
                'Signing entities have been successfully mapped to the profit center "PC US9682".'
            )
        );
        expect(mockEvict).toHaveBeenCalledWith({
            id: 'ROOT_QUERY',
            fieldName: 'abacusReferenceSapProfitCenterList',
        });
        expect(mockEvict).toHaveBeenCalledWith({
            id: 'ROOT_QUERY',
            fieldName: 'abacusSigningEntitiesBySapProfitCenter',
        });
    });

    it('shows an error message and does not toast or evict cached lists when submission fails', async () => {
        bulkCreateSigningEntities.mockRejectedValueOnce(new Error('failed'));
        renderComponent();

        selectSigningEntity('AWAL Digital Limited');
        fireEvent.click(screen.getByText('Add'));

        await screen.findByText(`Error: ${FAILED_TO_MAPPED_SIGNING_ENTITIES}`);
        expect(addToastMock).not.toHaveBeenCalled();
        expect(mockEvict).not.toHaveBeenCalled();
    });

    it('resets the form when the sidecar is reopened for a different profit center', () => {
        const { rerender } = renderComponent({
            ...defaultProps,
            isSidecarOpen: false,
        });

        rerender(
            <EditMappedSigningEntities
                {...defaultProps}
                profitCenter={{
                    displayName: 'AWAL Digital Limited : UK4914',
                    referenceSapProfitCenterId: '1',
                }}
                isSidecarOpen={true}
            />
        );

        expect(screen.getByText('AWAL Digital Limited : UK4914')).toBeDefined();
        expect(
            profitCenterQueries.useSigningEntitiesByProfitCenter
        ).toHaveBeenCalledWith(
            expect.objectContaining({ referenceSapProfitCenterId: '1' })
        );
    });

    it('closes the sidecar and clears the error when no operation is in flight', () => {
        renderComponent();

        fireEvent.click(screen.getByTestId('CloseGlyphIcon'));

        expect(defaultProps.setIsSidecarOpen).toHaveBeenCalledWith(false);
    });
});
