import React from 'react';
import { fireEvent } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import { testContribution } from 'lib/mockNrContributionsData';
import { range } from 'lodash';
import { MAIN_CONTENT_CLASSNAME } from 'src/constants';
import {
    Featured,
    NrContributionSearchOrderByField,
    NrContributorDeliveryStoreStatus,
    OrderDir,
    Priority,
} from 'src/data/globalTypes';
import * as contributionsQuery from 'src/data/queries/contributions/contributions';
import * as contributorsQuery from 'src/data/queries/getAllNrContributors/getAllNrContributors';
import * as contributorByIdQuery from 'src/data/queries/nrContributorById/nrContributorById';
import * as contributorSearchQuery from 'src/data/queries/nrContributorSearch/nrContributorSearch';
import * as featureFlags from 'src/utils/features';
import * as routeUtils from 'src/utils/route';
import CatalogPage, { CLASSNAME } from '../catalogPage';
import { PAGE_SIZE } from '../constants';
import * as pageParams from '../utils/contributionsParamsComposer';

describe('<CatalogPage>', () => {
    const renderPage = () =>
        renderInAppContext(
            <div className={MAIN_CONTENT_CLASSNAME}>
                <CatalogPage />
            </div>
        );

    const fetchMore = jest.fn();
    const setParamsSpy = jest.fn();
    const contributionsData: contributionsQuery.CatalogResults = {
        totalCount: 1,
        items: [testContribution],
    };

    const mockQueries = ({
        loading = false,
        data,
    }: {
        loading?: boolean;
        data?: contributionsQuery.CatalogResults;
    } = {}) => {
        jest.spyOn(
            contributionsQuery,
            'useGetContributionsQuery'
        ).mockReturnValue({
            data,
            loading,
            error: undefined,
            fetchMore,
            fetchingMore: false,
        });
        jest.spyOn(
            pageParams,
            'useContributionsParamsComposer'
        ).mockReturnValue({
            limit: PAGE_SIZE,
            filters: {
                account: undefined,
                isrc: undefined,
                participantId: undefined,
                priority: undefined,
                recordingTitle: undefined,
                contributorId: undefined,
                soundRecordingId: undefined,
            },
            orderBy: NrContributionSearchOrderByField.LAST_MODIFIED,
            orderDir: OrderDir.DESC,
        });
    };

    beforeEach(() => {
        setParamsSpy.mockClear();
        jest.spyOn(featureFlags, 'useUpload').mockReturnValue(true);
    });

    describe('on loading and no data', () => {
        beforeEach(() => {
            mockQueries({ loading: true, data: undefined });
        });

        afterEach(() => {
            jest.restoreAllMocks();
        });

        test('show page loading indicator', () => {
            const { container } = renderPage();
            const element = container.getElementsByClassName(
                'LoadingPageIndicator'
            );
            expect(element).toHaveLength(1);
        });
    });

    describe('on loading and has cached data', () => {
        beforeEach(() => {
            mockQueries({ loading: true, data: contributionsData });
        });

        afterEach(() => {
            jest.restoreAllMocks();
        });

        test('does not show page loading indicator', () => {
            const { container } = renderPage();

            const element = container.getElementsByClassName(
                'LoadingPageIndicator'
            );
            expect(element).toHaveLength(0);
        });
    });

    describe('once loaded and has data', () => {
        beforeEach(() => {
            mockQueries({ loading: false, data: contributionsData });
        });

        afterEach(() => {
            jest.restoreAllMocks();
        });

        test('renders default component', () => {
            const { container, getAllByTestId } = renderPage();
            const classname = container
                .getElementsByClassName(CLASSNAME)
                .item(0);

            expect(classname).toBeVisible();
            expect(getAllByTestId('FilterCatalogTextInput')[1]).toBeVisible();
            expect(
                getAllByTestId('Select_accountSearchDropdown')[1]
            ).toBeVisible();
        });

        test('renders Create New button', () => {
            const { container } = renderPage();
            const button = container
                .getElementsByClassName(`${CLASSNAME}-create`)
                .item(0);
            expect(button).toBeVisible();
        });

        test('calls contributions request', () => {
            jest.spyOn(
                pageParams,
                'useContributionsParamsComposer'
            ).mockReturnValue({
                limit: PAGE_SIZE,
                filters: {
                    account: undefined,
                    isrc: undefined,
                    participantId: undefined,
                    priority: undefined,
                    recordingTitle: undefined,
                    contributorId: undefined,
                    soundRecordingId: undefined,
                },
                orderBy: NrContributionSearchOrderByField.LAST_MODIFIED,
                orderDir: OrderDir.DESC,
            });
            renderPage();

            expect(
                contributionsQuery.useGetContributionsQuery
            ).toHaveBeenCalledWith({
                limit: PAGE_SIZE,
                filters: {
                    account: undefined,
                    isrc: undefined,
                    participantId: undefined,
                    priority: undefined,
                    recordingTitle: undefined,
                    soundRecordingId: undefined,
                    contributorId: undefined,
                },
                orderBy: NrContributionSearchOrderByField.LAST_MODIFIED,
                orderDir: OrderDir.DESC,
            });
        });

        test('renders CatalogTable', () => {
            const { container } = renderPage();
            const catalogTable = container
                .getElementsByClassName('CatalogTable')
                .item(0);

            expect(catalogTable).toBeVisible();
        });

        test('header has ViewBulkUploads button', () => {
            const { container } = renderPage();
            const uploadButton = container
                .getElementsByClassName('ViewBulkUploads')
                .item(0);

            expect(uploadButton).toBeVisible();
        });

        test('renders filters', () => {
            const { getAllByTestId } = renderPage();
            const accountDropdown = getAllByTestId(
                'Select_accountSearchDropdown'
            )[1];
            const priorityDropdown = getAllByTestId(
                'Select_priorityDropdown'
            )[1];
            const textFilter = getAllByTestId('FilterCatalogTextInput')[1];
            const contributorDropdown = getAllByTestId(
                'Select_contributorSearchDropdown'
            )[1];
            const participantDropdown = getAllByTestId(
                'Select_participantSearchDropdown'
            )[1];

            expect(accountDropdown).toBeVisible();
            expect(priorityDropdown).toBeVisible();
            expect(textFilter).toBeVisible();
            expect(contributorDropdown).toBeVisible();
            expect(participantDropdown).toBeVisible();
        });
    });

    describe('on sorting', () => {
        beforeEach(() => {
            mockQueries({ loading: true, data: contributionsData });
            jest.spyOn(routeUtils, 'useRouteParams').mockReturnValue([
                { orderBy: 'test' },
                setParamsSpy,
            ]);
        });

        afterEach(() => {
            jest.restoreAllMocks();
        });

        test('renders loadingIndicator', () => {
            const { container } = renderPage();
            expect(
                container.querySelector('.LoadingSpinner.show')
            ).toBeVisible();
        });
    });

    describe('when Contributor dropdown selected', () => {
        const queryData = [
            {
                id: 'u2491414214',
                name: 'Contributor A',
                stores: [
                    {
                        deliveryStore: {
                            name: 'test',
                        },
                        status: NrContributorDeliveryStoreStatus.REGISTERED,
                    },
                ],
            },
            {
                id: 'a123414142315',
                name: 'Contributor B',
                stores: [
                    {
                        deliveryStore: {
                            name: 'test',
                        },
                        status: NrContributorDeliveryStoreStatus.REGISTERED,
                    },
                ],
            },
        ];
        beforeEach(() => {
            mockQueries({ loading: false, data: contributionsData });
            jest.spyOn(
                contributorByIdQuery,
                'useNrContributorByIdQuery'
            ).mockReturnValue({
                data: queryData[0],
                loading: false,
                error: undefined,
            });
            jest.spyOn(
                contributorSearchQuery,
                'useNrContributorSearchQuery'
            ).mockReturnValue([
                jest.fn(),
                {
                    data: queryData,
                    loading: false,
                },
            ]);
            jest.spyOn(
                contributorsQuery,
                'useGetAllNrContributors'
            ).mockReturnValue({
                data: { totalCount: 2, contributors: queryData },
                isLoading: false,
                error: undefined,
                fetchMore: jest.fn(),
                fetchingMore: false,
            });
            jest.spyOn(
                pageParams,
                'useContributionsParamsComposer'
            ).mockReturnValue({
                limit: PAGE_SIZE,
                filters: {
                    account: undefined,
                    isrc: undefined,
                    participantId: undefined,
                    priority: undefined,
                    recordingTitle: undefined,
                    contributorId: 'u2491414214',
                    soundRecordingId: undefined,
                },
                orderBy: NrContributionSearchOrderByField.LAST_MODIFIED,
                orderDir: OrderDir.DESC,
            });
            jest.spyOn(routeUtils, 'useRouteParams').mockReturnValue([
                { selectedContributor: 'u2491414214' },
                setParamsSpy,
            ]);
        });

        afterEach(() => {
            jest.restoreAllMocks();
        });

        test('calls setParams handler and useGetContributionsQuery with selected Contributor', () => {
            const { getAllByTestId, getAllByText } = renderPage();
            const input = getAllByTestId(
                'Select_contributorSearchDropdown'
            )[1].querySelector('input');

            if (input)
                fireEvent.change(input, { target: { value: 'Contributor A' } });
            fireEvent.click(getAllByText('Contributor A')[1]);

            expect(
                contributionsQuery.useGetContributionsQuery
            ).toHaveBeenCalledWith({
                limit: PAGE_SIZE,
                filters: {
                    account: undefined,
                    isrc: undefined,
                    participantId: undefined,
                    priority: undefined,
                    recordingTitle: undefined,
                    soundRecordingId: undefined,
                    contributorId: 'u2491414214',
                },
                orderBy: NrContributionSearchOrderByField.LAST_MODIFIED,
                orderDir: OrderDir.DESC,
            });
        });
    });

    describe('when Priority dropdown selected', () => {
        beforeEach(() => {
            mockQueries({ loading: false, data: contributionsData });
            jest.spyOn(routeUtils, 'useRouteParams').mockReturnValue([
                { selectedPriority: 'HIGH' },
                setParamsSpy,
            ]);
            jest.spyOn(
                pageParams,
                'useContributionsParamsComposer'
            ).mockReturnValue({
                limit: PAGE_SIZE,
                filters: {
                    account: undefined,
                    isrc: undefined,
                    participantId: undefined,
                    priority: Priority.HIGH,
                    recordingTitle: undefined,
                    contributorId: undefined,
                    soundRecordingId: undefined,
                },
                orderBy: NrContributionSearchOrderByField.LAST_MODIFIED,
                orderDir: OrderDir.DESC,
            });
        });

        afterEach(() => {
            jest.restoreAllMocks();
        });

        test('calls setParams handler with selected priority', () => {
            const { getAllByTestId } = renderPage();
            const priorityFilter = getAllByTestId('Select_priorityDropdown')[1];
            const input = priorityFilter.querySelector('input');

            if (input) fireEvent.change(input, { target: { value: 'HIGH' } });
            expect(
                contributionsQuery.useGetContributionsQuery
            ).toHaveBeenCalledWith({
                limit: PAGE_SIZE,
                filters: {
                    account: undefined,
                    isrc: undefined,
                    participantId: undefined,
                    priority: 'HIGH',
                    recordingTitle: undefined,
                    soundRecordingId: undefined,
                    contributorId: undefined,
                },
                orderBy: NrContributionSearchOrderByField.LAST_MODIFIED,
                orderDir: OrderDir.DESC,
            });
        });
    });

    describe('infinite scroll', () => {
        const numRows = 50;
        const createContributionsData = (
            contribution: Partial<contributionsQuery.CatalogContributions> = {}
        ): contributionsQuery.CatalogContributions => ({
            accountId: 123,
            accountName: 'Big Hit Entertainment',
            contributorId: '987-afhiefaw',
            contributorName: 'Suga',
            contributorRelationship: Featured.MAIN_PERFORMER,
            countryOfContribution: 'Germany',
            countryOfContributionCode: 'DE',
            id: '123-abc',
            instruments: ['Piano'],
            isrc: 'NOO12345',
            firstReleaseYear: 2020,
            lastModifiedAt: '2022-04-12T09:15:49.257Z',
            primaryArtist: 'BTS',
            nrSrId: 'abcd-1234',
            performanceName: 'Stage Name',
            priority: Priority.LOW.toString(),
            srNameVersion: 'Dynamite (live)',
            valid: true,
            evidence: '',
            ...contribution,
        });
        const items = range(0, numRows).map(i =>
            createContributionsData({ id: `CONTRIB-ID-${i}` })
        );

        beforeEach(() => {
            mockQueries({ data: { totalCount: 100, items } });
        });

        afterEach(() => {
            jest.restoreAllMocks();
            fetchMore.mockClear();
        });

        const scrollMainContent = (container: HTMLElement) => {
            const mainContent = container
                .getElementsByClassName(MAIN_CONTENT_CLASSNAME)
                .item(0);
            if (!mainContent) throw new Error('MainContent not found');
            fireEvent.scroll(mainContent);
        };

        test('fetches more data when scrolling to bottom', () => {
            jest.useFakeTimers();
            const { container } = renderPage();

            scrollMainContent(container);
            jest.runOnlyPendingTimers();

            // TODO refactor this test. Always runs successfully. Add track event tests.
            setTimeout(() => {
                expect(fetchMore).toHaveBeenCalledWith({
                    variables: { limit: PAGE_SIZE, offset: numRows },
                });
            }, 1000);
        });
    });
});
