import React, {
    type FC,
    useCallback,
    useEffect,
    useRef,
    useState,
} from 'react';
import { LoadingSpinner, Pagination } from '@theorchard/suite-components';
import { Page } from '@theorchard/suite-frontend';
import dayjs from 'dayjs';
import StickyHeader from 'src/components/stickyHeader';
import { NrDeliveryOrderStatus } from 'src/data/globalTypes';
import {
    type DeliveryOrders,
    useGetDeliveryOrdersQuery,
} from 'src/data/queries';
import {
    ensureSortByType,
    ensureReDeliverType,
} from 'src/pages/monitorPage/utils';
import { type SortBy, type SortDirection } from 'src/types';
import {
    type SetParamsCallback,
    ensureSortDirectionType,
    type ParamValue,
} from 'src/utils';
import {
    recordGoToFirstPage,
    recordGoToLastPage,
    recordGoToNextPage,
    recordGoToPreviousPage,
    recordOrdersSortingByKey,
    recordRowsPerPageClick,
} from 'src/utils/segment';
import Filters from '../components/filters';
import {
    DEFAULT_SORT_DIR_ORDERS_PAGE,
    DEFAULT_SORT_FIELD_ORDERS_PAGE,
    PAGE_SIZE,
} from '../constants';
import { type MonitorRouteParams } from '../types';
import OrdersTable from './ordersTable';

export const CLASSNAME = 'Orders';

const pollingInterval = 60000;

interface Props {
    params: MonitorRouteParams;
    handleParams: SetParamsCallback;
}

const Orders: FC<Props> = ({ params, handleParams }) => {
    const [page, setPage] = useState(0);
    const [pageSize, setPageSize] = useState(PAGE_SIZE);
    const headerVisibilityThreshold = useRef<HTMLDivElement>(null);
    const { selectedOrder, selectedStore, reDeliver, orderBy, orderDir } =
        params;

    const {
        data,
        loading,
        error,
        startPolling,
        stopPolling,
        fetchMore,
        fetchingMore,
    } = useGetDeliveryOrdersQuery({
        filters: {
            cmoId: selectedStore,
            orderId: selectedOrder,
            reDeliver: ensureReDeliverType(reDeliver),
        },
        limit: pageSize,
        orderBy: ensureSortByType(orderBy),
        orderDir: ensureSortDirectionType(orderDir),
        offset: page * pageSize,
    });

    // we only care about the first 50 items b/c the limit passed into the query is set to 50.
    const orderStatuses = data?.items.map(o => o.status).slice(0, pageSize);

    const stopPollingContributions = useCallback(
        (orders: DeliveryOrders[] | undefined) => {
            if (!orders) return true;
            if (
                orderStatuses?.every(
                    s =>
                        s === NrDeliveryOrderStatus.ERROR ||
                        s === NrDeliveryOrderStatus.SUCCESS
                )
            )
                return true;
            const booleanArray = orders.map(o => {
                const dateTimeNow = dayjs().format('YYYY-MM-DDTHH:mm');
                const createdAt = dayjs(o.createdAt).format('YYYY-MM-DDTHH:mm');
                const difference = dayjs(dateTimeNow).diff(createdAt, 'hour');
                if (difference > 5) return true;
                return !(
                    (difference <= 5 &&
                        o.status === NrDeliveryOrderStatus.PROCESSING_JOBS) ||
                    o.status === NrDeliveryOrderStatus.STARTED ||
                    o.status === NrDeliveryOrderStatus.QUEUED
                );
            });
            return !booleanArray.includes(false);
        },
        [orderStatuses]
    );

    const filtersApplied = !!(selectedOrder || selectedStore || reDeliver);
    const sortingApplied =
        orderDir !== DEFAULT_SORT_DIR_ORDERS_PAGE ||
        orderBy !== DEFAULT_SORT_FIELD_ORDERS_PAGE;

    const handleSort = (sortBy: SortBy[]) => {
        recordOrdersSortingByKey(sortBy[0].key, sortBy[0].direction);
        handleParams({ orderBy: sortBy[0].key, orderDir: sortBy[0].direction });
    };

    const handleFetchMore = useCallback(
        async (limit: number, offset: number) => {
            await fetchMore({ variables: { limit, offset } });
        },
        [fetchMore]
    );

    const handleClearFilters = useCallback(() => {
        setPage(0);
        handleParams({
            orderId: undefined,
            store: undefined,
            reDeliver: undefined,
            tab: 'performance',
        });
    }, [handleParams]);

    const handleFiltersChange = useCallback(
        (filters: string | Record<string, ParamValue | undefined>) => {
            // reset pagination when filters change
            setPage(0);
            handleParams(filters);
        },
        [handleParams]
    );

    useEffect(() => {
        if (error) stopPolling();
    }, [error, stopPolling]);

    useEffect(() => {
        if (stopPollingContributions(data?.items)) stopPolling();
        else startPolling(pollingInterval);
    }, [startPolling, data, stopPolling, stopPollingContributions]);

    useEffect(() => {
        if (!loading)
            handleFetchMore(pageSize, page * pageSize).catch(error =>
                console.error(error)
            );
    }, [page, pageSize, loading, handleFetchMore]);

    const handlePageChange = useCallback(
        (currentPage: number) => {
            const shift = currentPage - page;
            const lastPage = Math.ceil((data?.totalCount ?? 0) / pageSize) - 1;

            if (shift === 1) {
                recordGoToNextPage();
            } else if (shift === -1) {
                recordGoToPreviousPage();
            } else if (currentPage === lastPage) {
                recordGoToLastPage();
            } else if (currentPage === 0) {
                recordGoToFirstPage();
            }

            setPage(currentPage);
        },
        [page, data?.totalCount, pageSize]
    );

    const handlePageSizeChange = useCallback((size: number) => {
        setPageSize(size);
        recordRowsPerPageClick(size);
    }, []);

    return (
        <div className={CLASSNAME}>
            <StickyHeader target={headerVisibilityThreshold}>
                <Filters setParams={handleFiltersChange} params={params} />
                <div
                    className={`${CLASSNAME}-sticky-row`}
                    style={{
                        paddingBottom: loading ? 10 : 0,
                        paddingTop: loading ? 10 : 0,
                    }}
                >
                    <Pagination
                        totalCount={data?.totalCount ?? 0}
                        currentPage={page}
                        pageSize={pageSize}
                        onChange={handlePageChange}
                        onSetPageSize={handlePageSizeChange}
                        loading={loading}
                    />
                    {loading && (
                        <div className={`${CLASSNAME}-sticky-row-status`}>
                            <LoadingSpinner
                                show
                                className={`${CLASSNAME}-sticky-row-status-icon`}
                            />
                            {$t('neighbouringRights.monitorPage.updatingTable')}
                        </div>
                    )}
                </div>
            </StickyHeader>
            <Filters setParams={handleFiltersChange} params={params} />
            <Page.View error={error} loading={loading} hasData={!!data}>
                <div ref={headerVisibilityThreshold} />
                <OrdersTable
                    orders={data?.items ?? []}
                    totalOrders={data?.totalCount}
                    loading={
                        (loading || fetchingMore) &&
                        (sortingApplied || filtersApplied)
                    }
                    onSort={handleSort}
                    sortBy={{
                        key: orderBy,
                        direction: orderDir as SortDirection,
                    }}
                    onClearFilters={handleClearFilters}
                    filtersApplied={filtersApplied}
                    page={page}
                    pageSize={pageSize}
                    handlePageChange={handlePageChange}
                    handlePageSizeChange={handlePageSizeChange}
                />
            </Page.View>
        </div>
    );
};

export default Orders;
