import React from 'react';
import * as apollo from '@apollo/client';
import { fireEvent, screen, waitFor } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import { Route } from 'react-router-dom';
import {
    assignedContributionsList,
    unassignedContributionsList,
} from 'src/__fixtures__/graphql/contributions';
import * as contributionsQuery from 'src/apollo/queries/nr-contributions';
import {
    sortByColumn,
    sortDirection,
} from 'src/apollo/type-constants/schedule';
import Contributions, {
    ContributionsPropTypes,
} from 'src/components/schedule-form/contributions';
import { CONTRIBUTIONS_LOADING_ERROR_MSG } from 'src/types/schedule';
import { editContributorSchedule } from 'src/urls/frontend-royalties';

const fetchNextExistingPage = jest.fn();
const contributionsResponse = (data: any) => ({
    data,
    error: undefined,
    loading: false,
    fetchMore: jest.fn(),
    fetchNextExistingPage,
});

describe('<Contributions>', () => {
    const defaultProps = {
        contributions: {
            unassignedContributions: [],
            assignedContributions: [],
        },
        setContributions: jest.fn(),
    };
    const render = (props: ContributionsPropTypes) =>
        renderInAppContext(
            <Route path="/contributor/:contributorId/schedule/:scheduleId/edit">
                <Contributions {...props} />
            </Route>,
            {
                pathname: editContributorSchedule(
                    '1ec7c1bf-2318-4052-9406-a3e35a620bd3',
                    '1'
                ),
            }
        );

    afterEach(jest.restoreAllMocks);

    beforeEach(() => {
        jest.spyOn(apollo, 'useApolloClient').mockReturnValue({
            cache: { evict: jest.fn() },
        } as unknown as apollo.ApolloClient<object>);

        jest.spyOn(
            contributionsQuery,
            'useNrContributionFragment'
        ).mockReturnValue({
            abacusScheduleId: null,
            id: 'd45a76bd-d868-4a3e-ba4d-75d4b2f203e2',
            nrContributor: {
                id: 'ff2a3bb5-61e2-41c9-b8fd-b33df78c3211',
            },
            nrSoundRecording: {
                id: 'f8da81d1-7749-4bf5-9b08-477c96019c58',
                isrc: 'GBGCT0700001',
                mainArtist: 'Charli XCX',
                recordingTitle: '!franchesckarr!',
            },
        });

        jest.spyOn(contributionsQuery, 'useNRContributionsList')
            .mockReturnValueOnce(
                contributionsResponse(unassignedContributionsList)
            )
            .mockReturnValue(contributionsResponse(assignedContributionsList));
    });

    it('renders Contributions containers"', async () => {
        render(defaultProps);
        await waitFor(() => {
            expect(screen.getByText('Unassigned Contributions')).toBeDefined();
            expect(screen.getByText('Assigned Contributions')).toBeDefined();
            expect(screen.getAllByText('Contributions (2 of 4)')).toBeDefined();
        });
    });

    it('renders a list', async () => {
        render(defaultProps);
        const unassignedCheckboxes = await screen.findAllByTestId(
            'ItemList-unassignedContributions-Checkbox'
        );
        const assignedCheckboxes = screen.queryAllByTestId(
            'ItemList-assignedContributions-Checkbox'
        );
        expect(unassignedCheckboxes.length).toEqual(2);
        expect(assignedCheckboxes.length).toEqual(2);
        expect(contributionsQuery.useNRContributionsList).toHaveBeenCalledTimes(
            6
        );
        expect(contributionsQuery.useNRContributionsList).toHaveBeenCalledWith(
            {
                filters: {
                    contributionId: undefined,
                    contributorId: '1ec7c1bf-2318-4052-9406-a3e35a620bd3',
                    hasAbacusSchedule: false,
                    mainArtist: undefined,
                    recordingTitle: undefined,
                    validContributions: true,
                },
                limit: 100,
                offset: 0,
                orderBy: sortByColumn['nrsrNameVersion'],
                orderDir: sortDirection['asc'],
            },
            expect.anything()
        );
        expect(contributionsQuery.useNRContributionsList).toHaveBeenCalledWith(
            {
                filters: {
                    contributionId: undefined,
                    contributorId: '1ec7c1bf-2318-4052-9406-a3e35a620bd3',
                    hasAbacusSchedule: true,
                    abacusScheduleId: 1,
                    mainArtist: undefined,
                    recordingTitle: undefined,
                    validContributions: true,
                },
                limit: 100,
                offset: 0,
                orderBy: sortByColumn['nrsrNameVersion'],
                orderDir: sortDirection['asc'],
            },
            expect.anything()
        );
    });

    it('renders move item button on click of unassigned contributions checkbox', async () => {
        render(defaultProps);

        const checkboxes = await screen.findAllByTestId(
            'ItemList-unassignedContributions-Checkbox'
        );
        fireEvent.click(checkboxes[0]);

        const moveButton = screen.getByTestId('ItemList-MoveItemsButton-Text');
        expect(moveButton.innerHTML).toEqual(
            '<span>Move Selected (1) to</span><br><span>ASSIGNED CONTRIBUTIONS TO</span>'
        );
    });

    it('renders move item button and Undo link on click of assigned contributions checkbox', async () => {
        jest.spyOn(
            contributionsQuery,
            'useNRContributionsList'
        ).mockReturnValueOnce(
            contributionsResponse(unassignedContributionsList)
        );
        render(defaultProps);

        const checkboxes = await screen.findAllByTestId(
            'ItemList-assignedContributions-Checkbox'
        );
        fireEvent.click(checkboxes[0]);

        const moveButton = await screen.findByTestId(
            'ItemList-MoveItemsButton-Text'
        );
        expect(moveButton.innerHTML).toEqual(
            '<span>Move Selected (1) to</span><br><span>UNASSIGNED CONTRIBUTIONS TO</span>'
        );

        fireEvent.click(moveButton);
        await waitFor(() => {
            expect(screen.getByText('1 Contribution(s) Moved')).toBeDefined();
            expect(screen.getByText('Undo')).toBeDefined();
            expect(defaultProps.setContributions).toHaveBeenCalled();
        });
    });

    it('renders counts based on results + removed contributions', async () => {
        render({
            contributions: {
                unassignedContributions: [],
                assignedContributions: ['d45a76bd-d868-4a3e-ba4d-75d4b2f203e2'],
            },
            setContributions: jest.fn(),
        });
        expect(screen.getByText('Contributions (3 of 4)')).toBeDefined();
        expect(screen.getByText('Contributions (1 of 4)')).toBeDefined();
    });

    it('allows you to select all', async () => {
        render(defaultProps);
        const checkbox = await screen.findByTestId(
            'ItemList-unassignedContributions-Checkbox-All'
        );
        fireEvent.click(checkbox);
        const moveButton = screen.getByTestId('ItemList-MoveItemsButton-Text');
        expect(moveButton.innerHTML).toEqual(
            '<span>Move Selected (2) to</span><br><span>ASSIGNED CONTRIBUTIONS TO</span>'
        );
        expect(fetchNextExistingPage).toHaveBeenCalled();
    });

    it('renders error message', async () => {
        const graphQLError = new apollo.ApolloError({
            graphQLErrors: undefined,
            networkError: null,
            errorMessage: 'GraphQL error',
        });
        jest.spyOn(contributionsQuery, 'useNRContributionsList')
            .mockReturnValueOnce({
                data: unassignedContributionsList,
                error: graphQLError,
                loading: false,
                fetchMore: jest.fn(),
                fetchNextExistingPage,
            })
            .mockReturnValue(contributionsResponse(assignedContributionsList));
        render(defaultProps);
        expect(screen.getByText(CONTRIBUTIONS_LOADING_ERROR_MSG)).toBeDefined();
    });
});
