import React from 'react';
import * as apollo from '@apollo/client';
import { fireEvent, screen, waitFor, within } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import { referenceSapProfitCentersList } from 'src/__fixtures__/graphql/reference-sap-profit-center';
import { referenceSigningEntities } from 'src/__fixtures__/graphql/reference-signing-entity';
import { useUpdateSapProfitCenter } from 'src/apollo/mutations/reference-sap-profit-center';
import * as referenceSapProfitCenterQuery from 'src/apollo/queries/reference-sap-profit-center';
import * as signingEntitiesQuery from 'src/apollo/queries/reference-signing-entity';
import { PROFIT_CENTER_ERROR_MESSAGE } from 'src/apollo/type-constants/reference-sap-profit-center';
import { ReferenceSapProfitCentersList } from 'src/components/reference-sap-profit-centers-list/reference-sap-profit-centers-list';
import { DEFAULT_NO_RESULTS_MSG } from 'src/constants';

jest.mock('src/apollo/mutations/reference-sap-profit-center', () => ({
    ...jest.requireActual('src/apollo/mutations/reference-sap-profit-center'),
    useUpdateSapProfitCenter: jest.fn(),
}));

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

    let requestSpy: jest.SpyInstance;
    let refetchSpy: jest.Mock;
    let updateSapProfitCenter: jest.Mock;

    beforeEach(() => {
        refetchSpy = jest.fn();
        requestSpy = jest
            .spyOn(
                referenceSapProfitCenterQuery,
                'useReferenceSapProfitCenters'
            )
            .mockReturnValue({
                data: referenceSapProfitCentersList,
                error: undefined,
                loading: false,
                refetch: refetchSpy,
            });

        updateSapProfitCenter = jest.fn().mockResolvedValue(undefined);
        (useUpdateSapProfitCenter as jest.Mock).mockReturnValue(
            updateSapProfitCenter
        );
    });

    const renderComponent = () =>
        renderInAppContext(
            <ReferenceSapProfitCentersList
                isSidecarOpen={false}
                setIsSidecarOpen={jest.fn()}
            />
        );

    it('requests profit centers list on render', () => {
        renderComponent();

        expect(requestSpy).toHaveBeenCalled();
    });

    it('renders a table headers', () => {
        renderComponent();

        const tableHeaders = [
            'Profit Center',
            'Display Name',
            'Company Code',
            'Mapped SEs',
        ];
        tableHeaders.forEach((header: any) =>
            expect(screen.getAllByText(header)).toBeDefined()
        );
    });

    it('renders a list of profit centers', () => {
        const bodyText: any = [];

        renderComponent();
        document
            .querySelectorAll('div.GridTable-cell')
            .forEach((body: any) => bodyText.push(body.textContent));

        expect(bodyText).toEqual([
            'US9682',
            'PC US9682',
            '2982',
            '-',
            '',

            'UK4914',
            'AWAL Digital Limited : UK4914',
            '4914',
            'AWAL Digital LimitedAWAL Recordings Licensing Ltd',
            '',
        ]);
    });

    it('renders error message', () => {
        const graphQLError = new apollo.ApolloError({
            graphQLErrors: undefined,
            networkError: null,
            errorMessage: 'GraphQL error',
        });
        jest.spyOn(
            referenceSapProfitCenterQuery,
            'useReferenceSapProfitCenters'
        ).mockReturnValue({
            data: {
                abacusReferenceSapProfitCenterList: {
                    items: [],
                    totalCount: 0,
                },
            },
            error: graphQLError,
            loading: false,
            refetch: jest.fn(),
        });

        renderComponent();
        expect(screen.getByText(PROFIT_CENTER_ERROR_MESSAGE)).toBeDefined();
        expect(screen.getByText(DEFAULT_NO_RESULTS_MSG)).toBeDefined();
    });

    it('renders filters', () => {
        renderComponent();

        expect(screen.getByText('Signing Entity')).toBeDefined();
        expect(
            screen.getByPlaceholderText(
                'Filter by Display Name or Profit Center'
            )
        ).toBeDefined();
        expect(screen.getByText('Show Unmapped Only')).toBeDefined();
    });

    it('renders "Clear Filter" link when checkbox is selected', () => {
        renderComponent();
        const checkbox = screen.getByRole('checkbox');
        fireEvent.click(checkbox);
        expect(screen.getByText('Clear filters')).toBeDefined();
    });

    it('renders "Clear Filter" link when search by profit center or display name', async () => {
        renderComponent();

        const input = screen.getByTestId('SearchInput-input');
        fireEvent.change(input, { target: { value: '123' } });
        await waitFor(() => {
            expect(screen.getByText('Clear filters')).toBeDefined();
        });
    });

    it('renders "Clear Filter" link when signing entity filter is selected', () => {
        jest.spyOn(
            signingEntitiesQuery,
            'useReferenceSigningEntities'
        ).mockReturnValue({
            data: referenceSigningEntities,
            loading: false,
            error: undefined,
        });
        renderComponent();

        const signingEntitySelect = screen.getByRole('combobox', {
            hidden: true,
        });
        fireEvent.click(signingEntitySelect);
        const selectSigningEntityOption = screen.getByText(
            'AWAL Recordings America, Inc.'
        );

        fireEvent.click(selectSigningEntityOption);

        const applyButton = screen.getByText('Apply');
        fireEvent.click(applyButton);

        expect(screen.getByText('Clear filters')).toBeDefined();
    });

    it('enters edit mode on the display name in a single click and saves the change', async () => {
        renderComponent();

        const editButton = screen.getAllByTestId(
            'EditableMetadata-edit-button'
        )[0];
        const cell = editButton.closest('.EditableMetadata') as HTMLElement;
        fireEvent.click(editButton);

        const input = within(cell).getByRole('textbox');
        expect(input).toHaveValue('PC US9682');

        fireEvent.change(input, { target: { value: 'Updated Name' } });
        fireEvent.click(
            within(cell).getByTestId('EditableMetadata-save-button')
        );

        expect(updateSapProfitCenter).toHaveBeenCalledWith({
            variables: {
                referenceSapProfitCenterId: '18',
                displayName: 'Updated Name',
            },
        });

        await waitFor(() => expect(refetchSpy).toHaveBeenCalled());
        expect(screen.getAllByTestId('read-input')[0]).toHaveTextContent(
            'Updated Name'
        );
    });
});
