import React, { useEffect, useRef, useState } from 'react';
import { PageHeader } from '@theorchard/suite-components';
import { Page, useIdentity } from '@theorchard/suite-frontend';
import { findIndex } from 'lodash';
import { useLocation, useParams } from 'react-router-dom';
import {
    ROUTE_REVIEW_QUEUE,
    ROUTE_REVIEW_QUEUE_PRODUCT_MODAL,
} from 'src/constants';
import { useLockReviewQueueItemMutation } from 'src/data/mutations/lockReviewQueueItem';
import { useUnlockReviewQueueItemMutation } from 'src/data/mutations/unlockReviewQueueItem';
import {
    useIdentityByIdQuery,
    useProductQuery,
    useProductValidationsQuery,
    useProductPricingQuery,
    useReviewQueueItemQuery,
} from 'src/data/queries';
import { useReviewQueueMultiSelectSearch } from 'src/data/queries/reviewQueueMultiSelectSearch/reviewQueueMultiSelectSearch';
import ProductModal from 'src/pages/productModal';
import ReviewQueueList from 'src/pages/reviewQueuePage/components/reviewQueueList';
import { useLockedProductError } from 'src/utils/features';
import { getReviewQueueQueryParams, useChangeURLPath } from 'src/utils/routev2';
import type { ApolloQueryResult } from '@apollo/client';
import type {
    ReviewQueueSearchItem,
    ReviewQueueSearchResults,
} from 'src/data/queries/reviewQueueSearch/types';
import type { SubmissionType } from 'src/types';
export const CLASS_NAME = 'ReviewQueuePage';
export const INTL_PREFIX = 'contentReview.reviewQueuePage';

interface ParamTypes {
    reviewQueueId: string;
    productId: string;
}

export const getNextReviewQueueItem = (
    reviewQueueSearch: ReviewQueueSearchResults | null,
    reviewQueueId: number | null,
    fetchMore: (
        variables: object
    ) => Promise<ApolloQueryResult<ReviewQueueSearchResults>>
): ReviewQueueSearchItem | null => {
    if (!reviewQueueSearch || !reviewQueueSearch.items.length || !reviewQueueId)
        return null;

    const { items, totalCount } = reviewQueueSearch;
    const reviewItemIndex = findIndex(
        items,
        reviewItem => reviewItem?.reviewQueueId === reviewQueueId
    );
    const nextIndex = (reviewItemIndex + 1) % items.length;

    const moreToFetch = items.length < totalCount;
    const withinFetchRange = nextIndex + 5 > items.length;

    if (moreToFetch && withinFetchRange)
        void fetchMore({ variables: { offset: items.length, limit: 50 } });

    if (!nextIndex) return null;

    return items[nextIndex] || null;
};

interface UsePrefetchOptions {
    reviewQueueId: number;
    productId: number;
}

export const usePrefetch = ({
    reviewQueueId,
    productId,
}: UsePrefetchOptions) => {
    useReviewQueueItemQuery(reviewQueueId);
    useProductQuery(productId, reviewQueueId);
    useProductPricingQuery(productId);
    useProductValidationsQuery(productId);
};

interface UseLockReviewQueueItemOptions {
    reviewQueueId: number;
    nextReviewQueueItemId: number;
    setIsReviewQueueItemLocked: (isReviewQueueItemLocked: boolean) => void;
    isReviewQueueSearchLoading: boolean;
    reviewQueueItemLoading: boolean;
    lockedByUserId?: string | null;
    currentUserId?: string | null;
    lockedUntilDatetime?: string | null;
    setWasExistingLockDetected: (wasExistingLockDetected: boolean) => void;
    lockedProductError: boolean;
}

const useLockReviewQueueItem = ({
    reviewQueueId,
    nextReviewQueueItemId,
    setIsReviewQueueItemLocked,
    isReviewQueueSearchLoading,
    reviewQueueItemLoading,
    lockedByUserId,
    currentUserId,
    lockedUntilDatetime,
    setWasExistingLockDetected,
    lockedProductError,
}: UseLockReviewQueueItemOptions) => {
    const navigateToReviewQueuePageV2 = useChangeURLPath({
        pathTemplate: ROUTE_REVIEW_QUEUE,
    });
    const navigateToNextReviewQueueItemV2 = useChangeURLPath({
        pathTemplate: ROUTE_REVIEW_QUEUE_PRODUCT_MODAL,
        pathParams: {
            reviewQueueId: nextReviewQueueItemId,
        },
    });
    const reviewQueueIdToUnlock = useRef(reviewQueueId);
    const triggerLockReviewQueueItemMutation =
        useLockReviewQueueItemMutation(reviewQueueId);
    const triggerUnlockReviewQueueItemMutation =
        useUnlockReviewQueueItemMutation(reviewQueueIdToUnlock.current);

    const now = new Date();

    const existingLockValid =
        lockedProductError &&
        lockedUntilDatetime &&
        lockedByUserId !== currentUserId &&
        new Date(lockedUntilDatetime).valueOf() > now.valueOf();

    useEffect(() => {
        if (existingLockValid) {
            setWasExistingLockDetected(true);
        }
    }, [existingLockValid]);

    useEffect(() => {
        if (
            !reviewQueueId ||
            isReviewQueueSearchLoading ||
            reviewQueueItemLoading
        )
            return () => {
                return;
            };

        let reviewLockTimeoutId: number | undefined;
        reviewQueueIdToUnlock.current = reviewQueueId;

        const lock = async () => {
            try {
                const { data } = await triggerLockReviewQueueItemMutation();

                setIsReviewQueueItemLocked(true);

                // this won't happen, but the type leaves it as a possibility, so we need to handle it
                if (!data?.lockReviewQueueItem?.lockedUntilDatetime)
                    throw new Error('No lock expiration date returned');

                const lockedUntilDatetime = new Date(
                    data.lockReviewQueueItem.lockedUntilDatetime
                );
                const timeUntilExpirationMs =
                    lockedUntilDatetime.valueOf() - new Date().valueOf();
                reviewLockTimeoutId = window.setTimeout(
                    () => navigateToReviewQueuePageV2(),
                    timeUntilExpirationMs - 60 * 1000 // redirect one minute before lock expires
                );
            } catch (error) {
                navigateToNextReviewQueueItemV2();
            }
        };

        if (!existingLockValid) void lock();

        return () => {
            if (!reviewLockTimeoutId) return;
            window.clearTimeout(reviewLockTimeoutId);
            setIsReviewQueueItemLocked(false);
            void triggerUnlockReviewQueueItemMutation();
        };
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [reviewQueueId, isReviewQueueSearchLoading, reviewQueueItemLoading]);
};

const useGetSearchResults = (locationSearch: string) => {
    const reviewSearchVariables = getReviewQueueQueryParams(locationSearch);
    // We do this because we display POTENTIAL_AUDIO_INFRINGEMENT and CROSS_ACCOUNT_OSR_CONFLICT both as POTENTIAL_AUDIO_INFRINGEMENT
    // SEE REVIEW-1904
    reviewSearchVariables?.facets?.map(f => {
        if (f.name == 'notices.code') {
            if (f.values.includes('POTENTIAL_AUDIO_INFRINGEMENT')) {
                f.values.push('CROSS_ACCOUNT_OSR_CONFLICT');
            }
        }
    });

    const { loading, data, refetch, fetchMore, networkStatus } =
        useReviewQueueMultiSelectSearch(reviewSearchVariables);

    return {
        isReviewQueueSearchLoading: loading,
        reviewQueueSearch: data,
        refetchReviewQueueSearch: refetch,
        fetchMoreReviewQueueSearch: fetchMore,
        networkStatus,
    };
};

const ReviewQueuePage = () => {
    const { reviewQueueId: stringReviewQueueId } = useParams<ParamTypes>();
    const reviewQueueId = Number(stringReviewQueueId);
    const location = useLocation();
    const lockedProductError = useLockedProductError();

    const {
        error: reviewQueueItemQueryError,
        data: reviewQueueItem,
        loading: reviewQueueItemLoading,
    } = useReviewQueueItemQuery(reviewQueueId);

    const { id } = useIdentity();
    const { data: identity } = useIdentityByIdQuery(
        reviewQueueItem?.lockedByUserId
    );
    const lockedByUserName = identity
        ? `${identity.firstName ?? ''} ${identity.lastName ?? ''}`.trim()
        : undefined;

    const {
        isReviewQueueSearchLoading,
        reviewQueueSearch,
        refetchReviewQueueSearch,
        fetchMoreReviewQueueSearch,
        networkStatus,
    } = useGetSearchResults(location.search);

    const reviewQueueItems = reviewQueueSearch?.items || null;
    const nextReviewQueueItem = getNextReviewQueueItem(
        reviewQueueSearch,
        reviewQueueId || null,
        fetchMoreReviewQueueSearch
    );
    const [isReviewQueueItemLocked, setIsReviewQueueItemLocked] =
        useState(false);
    const [wasExistingLockDetected, setWasExistingLockDetected] =
        useState(false);

    const reviewQueueItemToPrefetch =
        nextReviewQueueItem || (reviewQueueItems && reviewQueueItems[0]);
    usePrefetch({
        productId: Number(reviewQueueItemToPrefetch?.productId),
        reviewQueueId: Number(reviewQueueItemToPrefetch?.reviewQueueId),
    });

    useLockReviewQueueItem({
        reviewQueueId,
        nextReviewQueueItemId: Number(nextReviewQueueItem?.reviewQueueId),
        setIsReviewQueueItemLocked,
        isReviewQueueSearchLoading,
        reviewQueueItemLoading,
        lockedByUserId: reviewQueueItem?.lockedByUserId ?? null,
        currentUserId: id ?? null,
        lockedUntilDatetime: reviewQueueItem?.lockedUntilDatetime ?? null,
        setWasExistingLockDetected,
        lockedProductError,
    });

    useEffect(() => {
        // refetch when product modal is closed
        if (reviewQueueId) return;
        void refetchReviewQueueSearch();
        // perform a followup fetch after the search index has had time to update.
        setTimeout(() => {
            void refetchReviewQueueSearch();
        }, 1500);
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [reviewQueueId]);

    return (
        <Page className={CLASS_NAME}>
            <PageHeader
                mainContent={{ title: $t(`${INTL_PREFIX}.title`) }}
                topMetadata={{ left: () => null }}
            />
            <ReviewQueueList
                reviewSearchQueryData={reviewQueueSearch}
                networkStatus={networkStatus}
                fetchMore={fetchMoreReviewQueueSearch}
            />
            <ProductModal
                reviewQueueItems={reviewQueueItems}
                currentReviewQueueItemId={reviewQueueId}
                nextReviewQueueItemId={
                    nextReviewQueueItem?.reviewQueueId || null
                }
                productId={reviewQueueItem?.productId}
                queueName={reviewQueueItem?.queueName || undefined}
                submissionType={
                    reviewQueueItem?.submissionType as SubmissionType
                }
                reviewQueueItemQueryError={reviewQueueItemQueryError}
                isReviewQueueItemLocked={isReviewQueueItemLocked}
                productTargetGroup={
                    reviewQueueItem?.movedToTarget?.targetGroup || null
                }
                submittedByUserLanguage={
                    reviewQueueItem?.submittedByIdentity?.localization ?? null
                }
                lockedByUserName={lockedByUserName}
                wasExistingLockDetected={wasExistingLockDetected}
            />
        </Page>
    );
};

export default ReviewQueuePage;
