import React from 'react';
import { fireEvent } from '@testing-library/react';
import { renderInAppContext } from '@theorchard/suite-testing';
import { range } from 'lodash';
import { MAIN_CONTENT_CLASSNAME } from 'src/constants';
import {
    NrDeliveryJobStatus,
    NrDeliveryOrderStatus,
} from 'src/data/globalTypes';
import { GetDeliveryStores_deliveryStoresV2_items as DeliveryStore } from 'src/data/queries/getNrDeliveryStores/__generated__/GetDeliveryStores';
import * as deliveryStoresQuery from 'src/data/queries/getNrDeliveryStores/nrDeliveryStores';
import * as jobsQuery from 'src/data/queries/nrDeliveryJobs/nrDeliveryJobs';
import { PAGE_SIZE } from 'src/pages/monitorPage/constants';
import { MonitorRouteParams } from 'src/pages/monitorPage/types';
import * as routeUtils from 'src/utils/route';
import Jobs, { CLASSNAME } from '../jobs';

describe('<Jobs>', () => {
    const setParamsSpy = jest.fn();
    const onFetchMore = jest.fn();
    const params = {
        showBy: 'jobs',
        selectedOrder: '',
        selectedStore: '',
        reDeliver: '',
        selectedStatus: '',
        selectedContributor: '',
        orderBy: 'createdAt',
        orderDir: 'desc',
    } as MonitorRouteParams;

    const jobData: jobsQuery.JobResult = {
        totalCount: 1,
        items: [
            {
                cmo: 'Gramo',
                contributorId: '705503f2-7dd6-426d-9637-efd26900d5ec',
                jobId: '767',
                lastUpdated: '2022-04-12T09:15:49.257Z',
                lastUpdatedBy: 'Someone',
                numberOfClaims: 235,
                name: 'Pineda, Allan',
                orderId: '187',
                orderCreatedAt: '2022-04-09T09:15:49.257Z',
                orderCreatedBy: 'Person',
                status: NrDeliveryJobStatus.SUCCESS,
            },
        ],
    };

    const renderPage = () =>
        renderInAppContext(
            <div className={MAIN_CONTENT_CLASSNAME}>
                <Jobs params={params} handleParams={setParamsSpy} />
            </div>
        );

    const mockQueries = ({
        loading = false,
        data,
    }: {
        loading?: boolean;
        data?: jobsQuery.JobResult;
    } = {}) => {
        jest.spyOn(jobsQuery, 'useGetNrDeliveryJobsQuery').mockReturnValue({
            data,
            loading,
            error: undefined,
            onFetchMore,
            fetchingMore: false,
        });
    };

    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('LoadingIndicator');
            expect(element).toHaveLength(1);
        });
    });

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

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

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

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

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

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

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

            expect(classname).toBeVisible();
        });

        test('calls jobs query with orderStatus', () => {
            renderPage();

            expect(jobsQuery.useGetNrDeliveryJobsQuery).toHaveBeenCalledWith({
                filters: {
                    cmoId: '',
                    contributorId: '',
                    orderId: '',
                    status: undefined,
                    orderStatus: [
                        NrDeliveryOrderStatus.PROCESSING_JOBS,
                        NrDeliveryOrderStatus.STARTED,
                        NrDeliveryOrderStatus.SUCCESS,
                    ],
                },
                orderBy: undefined,
                orderDir: 'DESC',
                limit: PAGE_SIZE,
                offset: 0,
            });
        });

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

            expect(jobsTable).toBeVisible();
        });

        test('renders filters', () => {
            const { container } = renderPage();
            const orderIdDropdown = container.querySelectorAll(
                '[data-testid="Dropdown_orderIdSearchDropdown"]'
            );
            const deliveryStoresDropdown = container.querySelectorAll(
                '[data-testid="Dropdown_deliveryStoresDropdown"]'
            );

            const contributorDropdown = container.querySelectorAll(
                '[data-testid="Dropdown_contributorSearchDropdown"]'
            );
            const statusDropdown = container.querySelectorAll(
                '[data-testid="Dropdown_deliveryStatusDropdown"]'
            );

            expect(orderIdDropdown).toHaveLength(2);
            orderIdDropdown.forEach(e => expect(e).toBeVisible());

            expect(deliveryStoresDropdown).toHaveLength(2);
            deliveryStoresDropdown.forEach(e => expect(e).toBeVisible());

            expect(contributorDropdown).toHaveLength(2);
            contributorDropdown.forEach(e => expect(e).toBeVisible());

            expect(statusDropdown).toHaveLength(2);
            statusDropdown.forEach(e => expect(e).toBeVisible());
        });
    });

    describe('when Delivery Store dropdown selected', () => {
        const queryData = [
            { id: '1277', name: 'GVL' },
            { id: '593', name: 'PPL' },
        ] as DeliveryStore[];
        beforeEach(() => {
            mockQueries({ loading: false, data: jobData });
            jest.spyOn(
                deliveryStoresQuery,
                'useNrDeliveryStoresQuery'
            ).mockReturnValue({
                data: queryData,
                loading: false,
                error: undefined,
            });
            jest.spyOn(routeUtils, 'useRouteParams').mockReturnValue([
                { store: '593' },
                setParamsSpy,
            ]);
        });

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

        test('calls setParams handler and useNrDeliveryStoresQuery with selected Store', () => {
            const { container, getByText } = renderPage();
            const input = container
                .querySelectorAll(
                    '[data-testid="Dropdown_deliveryStoresDropdown"]'
                )
                .item(0)
                .querySelector('input');

            if (input) fireEvent.change(input, { target: { value: 'PPL' } });
            fireEvent.click(getByText('PPL'));

            expect(setParamsSpy).toHaveBeenCalledWith({ store: '593' });
            expect(
                deliveryStoresQuery.useNrDeliveryStoresQuery
            ).toHaveBeenCalled();
        });
    });

    describe('infinite scroll', () => {
        const numRows = PAGE_SIZE;
        const createJobsData = (
            job: Partial<jobsQuery.DeliveryJobs> = {}
        ): jobsQuery.DeliveryJobs => ({
            cmo: 'Gramo',
            contributorId: '705503f2-7dd6-426d-9637-efd26900d5ec',
            jobId: '767',
            lastUpdated: '2022-04-12T09:15:49.257Z',
            lastUpdatedBy: 'Someone',
            numberOfClaims: 235,
            name: 'Pineda, Allan',
            orderId: '187',
            orderCreatedAt: '2022-04-09T09:15:49.257Z',
            orderCreatedBy: 'Person',
            status: NrDeliveryJobStatus.COMPLETE,
            ...job,
        });
        const items = range(0, numRows).map(i =>
            createJobsData({ jobId: `JOB-ID-${i}` })
        );

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

        afterEach(() => {
            jest.restoreAllMocks();
            onFetchMore.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();

            setTimeout(() => {
                expect(onFetchMore).toHaveBeenCalledWith({
                    variables: { limit: PAGE_SIZE, offset: numRows },
                });
            }, 1000);
        });
    });

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

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

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