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 {
    NrDeliveryOrdersOrderBy,
    NrDeliveryOrderStatus,
    OrderDir,
} from 'src/data/globalTypes';
import { OrdersResult, DeliveryOrders } from 'src/data/queries';
import { GetDeliveryStores_deliveryStoresV2_items as DeliveryStore } from 'src/data/queries/getNrDeliveryStores/__generated__/GetDeliveryStores';
import * as deliveryStoresQuery from 'src/data/queries/getNrDeliveryStores/nrDeliveryStores';
import * as ordersQuery from 'src/data/queries/nrDeliveryOrders/nrDeliveryOrders';
import * as routeUtils from 'src/utils/route';
import { PAGE_SIZE } from '../../constants';
import { MonitorRouteParams } from '../../types';
import Orders, { CLASSNAME } from '../orders';

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

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

    const ordersData: OrdersResult = {
        totalCount: 1,
        items: [
            {
                cmo: 'GVL',
                createdAt: '2022-04-12T09:15:49.257Z',
                lastUpdated: '2022-04-14T21:00:00.257Z',
                createdBy: 'Allan Moon',
                orderId: '53',
                outputFile: 'delivery/claims/orders/53/order_53.zip',
                numberOfJobs: 5,
                reDeliver: 'Yes',
                status: NrDeliveryOrderStatus.STARTED,
            },
        ],
    };

    const mockQueries = ({
        loading = false,
        data,
    }: {
        loading?: boolean;
        data?: OrdersResult;
    } = {}) => {
        jest.spyOn(ordersQuery, 'useGetDeliveryOrdersQuery').mockReturnValue({
            data,
            loading,
            error: undefined,
            fetchMore,
            fetchingMore: false,
            startPolling: jest.fn(),
            stopPolling: jest.fn(),
        });
    };

    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: ordersData });
        });

        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: ordersData });
        });

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

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

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

        test('calls orders query', () => {
            renderPage();

            expect(ordersQuery.useGetDeliveryOrdersQuery).toHaveBeenCalledWith({
                filters: {
                    cmoId: '',
                    orderId: '',
                    reDeliver: undefined,
                },
                limit: PAGE_SIZE,
                orderBy: NrDeliveryOrdersOrderBy.CREATED_AT,
                orderDir: OrderDir.DESC,
                offset: 0,
            });
        });

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

            expect(ordersTable).toBeVisible();
        });

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

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

    describe('infinite scroll', () => {
        const numRows = PAGE_SIZE;
        const createOrdersData = (
            order: Partial<DeliveryOrders> = {}
        ): DeliveryOrders => ({
            cmo: 'GVL',
            createdAt: '2022-04-12T09:15:49.257Z',
            lastUpdated: '2022-04-14T21:00:00.257Z',
            createdBy: 'Allan Moon',
            orderId: '53',
            outputFile: 'delivery/claims/orders/53/order_53.zip',
            numberOfJobs: 5,
            reDeliver: 'Yes',
            status: NrDeliveryOrderStatus.STARTED,
            ...order,
        });
        const items = range(0, numRows).map(i =>
            createOrdersData({ orderId: `ORDER-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();

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

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

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

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

    describe('when Delivery Store dropdown selected', () => {
        const queryData = [
            { id: '1277', name: 'GVL' },
            { id: '593', name: 'PPL' },
        ] as DeliveryStore[];
        beforeEach(() => {
            mockQueries({ loading: false, data: ordersData });
            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();
        });
    });
});
