import React, { useCallback, useEffect, useState } from 'react';
import { Page, type QueryParamsConfig } from '@theorchard/suite-frontend';
import { ROUTE_QUEUE_PERFORMANCE } from 'src/constants';
import { OrderDir, NrDeliveryQueueOrderBy } from 'src/data/globalTypes';
import {
    useGetDeliveryQueueItems,
    useContributionSearchLazyQuery,
} from 'src/data/queries';
import { type SortBy, type SortDirection } from 'src/types';
import { type ParamValue, useRouteParams } from 'src/utils/route';
import {
    recordGoToFirstPage,
    recordGoToLastPage,
    recordGoToNextPage,
    recordGoToPreviousPage,
    recordRowsPerPageClick,
} from 'src/utils/segment';
import Filters from './components/filters';
import {
    DEFAULT_SORT_DIR,
    DEFAULT_SORT_FIELD_QUEUE_PAGE,
    PAGE_SIZE,
} from './constants';
import QueueTable from './queueTable';

export const CLASSNAME = 'QueuePage';

export interface QueueRouteParams {
    selectedContributor: string;
    selectedIsrc: string;
    selectedRecordingId: string;
    selectedRecordingTitle: string;
    selectedStore: string;
    orderBy: string;
    orderDir: string;
}

export const PAGE_QUERY_PARAMS: QueryParamsConfig = {
    selectedContributor: { name: 'contributor', type: 'string' },
    selectedIsrc: { name: 'isrc', type: 'string' },
    selectedRecordingId: { name: 'soundRecordingId', type: 'string' },
    selectedRecordingTitle: { name: 'recordingTitle', type: 'string' },
    selectedStore: { name: 'store', type: 'string' },
    orderBy: { name: 'orderBy', default: 'lastUpdatedAt', type: 'string' },
    orderDir: {
        name: 'orderDir',
        default: `${DEFAULT_SORT_DIR}`,
        type: 'string',
    },
};

const ensureSortDirection = (direction: string): OrderDir | undefined => {
    if (direction === 'desc') return OrderDir.DESC;
    if (direction === 'asc') return OrderDir.ASC;
    return undefined;
};

const ensureOrderBy = (sortBy: string): NrDeliveryQueueOrderBy | undefined => {
    if (sortBy === 'createdAt') return NrDeliveryQueueOrderBy.CREATED_AT;
    if (sortBy === 'lastUpdatedAt') return NrDeliveryQueueOrderBy.LAST_UPDATED;
    return undefined;
};

const QueuePage = () => {
    const [page, setPage] = useState(0);
    const [pageSize, setPageSize] = useState(PAGE_SIZE);
    const [contributionIds, setContributionIds] = useState<string[] | null>(
        null
    );
    const [params, setParams] = useRouteParams<QueueRouteParams>(
        ROUTE_QUEUE_PERFORMANCE,
        { queryParams: PAGE_QUERY_PARAMS }
    );

    const {
        orderBy,
        orderDir,
        selectedContributor,
        selectedStore,
        selectedRecordingId,
        selectedRecordingTitle,
        selectedIsrc,
    } = params;
    const { data, totalCount, loading, error, fetchMore, fetchingMore } =
        useGetDeliveryQueueItems({
            filters: {
                cmoId: selectedStore,
                contributorUuid: selectedContributor,
                contributionUuids: contributionIds,
            },
            limit: pageSize,
            orderBy: ensureOrderBy(orderBy),
            orderDir: ensureSortDirection(orderDir),
            offset: page * pageSize,
        });

    const [
        doContributionsSearch,
        { data: contributionUuids, loading: contributionsLoading },
    ] = useContributionSearchLazyQuery();

    const filtersApplied = !!(
        selectedContributor ||
        selectedStore ||
        selectedRecordingId ||
        selectedRecordingTitle ||
        selectedIsrc
    );
    const sortingApplied =
        orderDir !== DEFAULT_SORT_DIR ||
        orderBy !== DEFAULT_SORT_FIELD_QUEUE_PAGE;

    const handleSort = (sortBy: SortBy[]) => {
        setParams({ 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);
        setParams({
            contributor: undefined,
            store: undefined,
            soundRecordingId: undefined,
            recordingTitle: undefined,
            isrc: undefined,
        });
        setContributionIds(null);
    }, [setParams, setContributionIds]);

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

    useEffect(() => {
        if (selectedRecordingTitle || selectedRecordingId || selectedIsrc) {
            doContributionsSearch({
                variables: {
                    filters: {
                        recordingTitle: selectedRecordingTitle ?? null,
                        soundRecordingId: selectedRecordingId ?? null,
                        isrc: selectedIsrc ?? null,
                    },
                },
            });
        }
    }, [
        selectedRecordingTitle,
        selectedRecordingId,
        selectedIsrc,
        doContributionsSearch,
    ]);

    useEffect(() => {
        if (
            (selectedRecordingTitle || selectedRecordingId || selectedIsrc) &&
            contributionUuids?.length
        )
            setContributionIds(contributionUuids);
    }, [
        selectedRecordingTitle,
        selectedRecordingId,
        selectedIsrc,
        contributionUuids,
    ]);

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

    const handlePageChange = useCallback(
        (currentPage: number) => {
            const shift = currentPage - page;
            const lastPage = Math.ceil((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, totalCount, pageSize]
    );

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

    return (
        <Page className={CLASSNAME}>
            <Page.Header title={$t('navigation.queue')} />
            <Filters
                setParams={handleFiltersChange}
                params={params}
                setContributionIds={setContributionIds}
            />
            <Page.View error={error} loading={loading} hasData={!!data}>
                <QueueTable
                    queueItems={data}
                    loading={
                        (loading || fetchingMore || contributionsLoading) &&
                        (sortingApplied || filtersApplied)
                    }
                    onSort={handleSort}
                    sortBy={{
                        key: orderBy,
                        direction: orderDir as SortDirection,
                    }}
                    onClearFilters={handleClearFilters}
                    filtersApplied={filtersApplied}
                    page={page}
                    pageSize={pageSize}
                    handlePageChange={handlePageChange}
                    handlePageSizeChange={handlePageSizeChange}
                    total={totalCount}
                />
            </Page.View>
        </Page>
    );
};

export default QueuePage;
