'use client';

import { useGateValue } from '@statsig/react-bindings';
import { useQueryClient } from '@tanstack/react-query';
import { useState } from 'react';
import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
} from 'react';

import { ModalTypes } from '@/components/modal/constants/ModalTypes';
import { toast } from '@/components/toast/Toast';
import { ToastV2Props } from '@/components/toast/ToastV2';
import { useModalContext } from '@/context/ModalContext';

import {
  DismissPendingButton,
  SparklesImageComponent,
  ThumbnailImageComponent,
  ViewGenerationButton,
} from './GenerationNotificationToast';
import { useGenerationPolling } from './useGenerationPolling';

const MAX_COMPLETED_BATCHES = 10;

interface GenerationNotificationContextValue {
  /**
   * Enable toast notifications
   */
  enablePendingToasts: () => void;

  /**
   * Disable toast notifications
   */
  disablePendingToasts: () => void;
}

const GenerationNotificationContext =
  createContext<GenerationNotificationContextValue | null>(null);

export function useGenerationNotifications() {
  const context = useContext(GenerationNotificationContext);
  if (!context) {
    throw new Error(
      'useGenerationNotifications must be used within GenerationNotificationProvider'
    );
  }
  return context;
}

export const GenerationNotificationProvider: React.FC<
  React.PropsWithChildren
> = ({ children }) => {
  const queryClient = useQueryClient();
  const { isModalOpen, openModalWithData } = useModalContext();
  const [showPendingToasts, setShowPendingToasts] = useState<boolean>(true);
  const enableGenerateCovers = useGateValue('gen-video-covers');

  // Track which clip+batch combinations have shown completion toasts (to avoid duplicates)
  const completedBatchesRef = useRef<Set<string>>(new Set());

  // Track the most recent toast ID so we can programmatically close it
  const currentToastRef = useRef<ReturnType<typeof toast> | undefined>(
    undefined
  );

  const isUpdateClipModalOpen = isModalOpen(ModalTypes.UPDATE_CLIP_METADATA);
  const isPublishSongModalOpen = isModalOpen(ModalTypes.PUBLISH_SONG);
  const isGenerateCoverArtModalOpen = isModalOpen(
    ModalTypes.GENERATE_COVER_ART
  );
  const isGenerationModalOpen =
    isUpdateClipModalOpen ||
    isPublishSongModalOpen ||
    isGenerateCoverArtModalOpen;

  // Only poll when modal is closed and we have clips to monitor
  const shouldPoll = !isGenerationModalOpen && enableGenerateCovers;

  // Use the polling hook
  const { finishedClipBatches, pendingBatchIds } =
    useGenerationPolling(shouldPoll);

  // Close the current toast programmatically (not user-initiated)
  const closeProgrammatically = useCallback(() => {
    if (currentToastRef.current) {
      const toastId = currentToastRef.current;
      currentToastRef.current = undefined;
      try {
        toast.close(toastId);
      } catch (error) {
        currentToastRef.current = toastId;
        throw error;
      }
    }
  }, [currentToastRef]);

  const handleUserCloseToast = useCallback(() => {
    currentToastRef.current = undefined;
  }, []);

  // Handler for when user clicks the X button - disable future toasts
  const handleUserClosePendingToast = useCallback(() => {
    setShowPendingToasts(false);
    closeProgrammatically();
  }, [closeProgrammatically]);

  // Handler to open modal with specific clipId and navigate to generation history
  const handleViewGeneration = useCallback(
    (clipId: string) => {
      let modalType = ModalTypes.GENERATE_COVER_ART;
      if (isGenerateCoverArtModalOpen) {
        modalType = isPublishSongModalOpen
          ? ModalTypes.PUBLISH_SONG
          : isUpdateClipModalOpen
            ? ModalTypes.UPDATE_CLIP_METADATA
            : ModalTypes.GENERATE_COVER_ART;
      }
      openModalWithData(
        modalType,
        {
          clipId,
        },
        'GenerationNotification'
      );
    },
    [
      openModalWithData,
      isGenerateCoverArtModalOpen,
      isPublishSongModalOpen,
      isUpdateClipModalOpen,
    ]
  );

  // Handle polling results
  useEffect(() => {
    if (!shouldPoll) return;

    const hasCompletions = Object.keys(finishedClipBatches).length > 0;

    if (hasCompletions) {
      // Find the first new completion, prioritizing batches where all items succeeded
      let firstNewCompletion: { clipId: string; batchId: string } | null = null;
      let firstFullySuccessfulCompletion: {
        clipId: string;
        batchId: string;
      } | null = null;

      for (const [clipId, batches] of Object.entries(finishedClipBatches)) {
        for (const batchId of Object.keys(batches)) {
          const key = `${clipId}:${batchId}`;
          if (!completedBatchesRef.current.has(key)) {
            completedBatchesRef.current.add(key);

            // Track the first completion overall
            if (!firstNewCompletion) {
              firstNewCompletion = { clipId, batchId };
            }

            // Track the first fully successful completion (all items complete)
            if (
              !firstFullySuccessfulCompletion &&
              batches[batchId]?.length > 0 &&
              batches[batchId]?.every((item) => item.status === 'complete')
            ) {
              firstFullySuccessfulCompletion = { clipId, batchId };
            }
          }
        }
      }

      // Prefer fully successful completion, fall back to first completion
      const selectedCompletion =
        firstFullySuccessfulCompletion || firstNewCompletion;

      // prevent completion toast from growing too large
      if (completedBatchesRef.current.size > MAX_COMPLETED_BATCHES) {
        const entries = Array.from(completedBatchesRef.current);
        completedBatchesRef.current = new Set(
          entries.slice(-MAX_COMPLETED_BATCHES)
        );
      }

      // If we found a new completion show toast
      if (selectedCompletion) {
        const { clipId } = selectedCompletion;

        // Close processing toast programmatically (we're replacing it with completion toast)
        closeProgrammatically();

        // Get thumbnail for the completion toast
        const thumbnailUrl = selectedCompletion?.batchId
          ? finishedClipBatches[clipId]?.[selectedCompletion.batchId]?.[0]
              ?.thumbnailUrl
          : undefined;

        // Show completion toast
        currentToastRef.current = toast({
          title: 'Video Covers Generation Complete!',
          description: undefined,
          status: 'info',
          duration: null,
          isClosable: true,
          position: 'top',
          className: 'w-[656px]',
          closeButtonClassName: 'absolute top-1/2 -translate-y-1/2 right-4',
          onCloseClick: handleUserCloseToast,
          actionComponent: () => (
            <ViewGenerationButton
              clipId={clipId}
              onClick={handleViewGeneration}
            />
          ),
          imageComponent: thumbnailUrl
            ? () => <ThumbnailImageComponent thumbnailUrl={thumbnailUrl} />
            : undefined,
        } as ToastV2Props);
      }
    } else if (
      pendingBatchIds.length > 0 &&
      !currentToastRef.current &&
      showPendingToasts
    ) {
      // Still processing - show toast if not already shown AND user wants toasts
      currentToastRef.current = toast({
        title: 'Generating Video Covers',
        description: 'You will be notified when done',
        status: 'info',
        duration: null,
        isClosable: false,
        className: 'w-[656px]',
        position: 'top',
        imageComponent: SparklesImageComponent,
        actionComponent: () => (
          <DismissPendingButton onClick={handleUserClosePendingToast} />
        ),
      } as ToastV2Props);
    }
  }, [
    finishedClipBatches,
    pendingBatchIds,
    shouldPoll,
    queryClient,
    handleViewGeneration,
    closeProgrammatically,
    handleUserCloseToast,
    handleUserClosePendingToast,
    showPendingToasts,
  ]);

  // Close toast when modal opens
  useEffect(() => {
    if (isGenerationModalOpen) {
      closeProgrammatically();
    }
  }, [isGenerationModalOpen, closeProgrammatically]);

  const enablePendingToasts = useCallback(() => {
    setShowPendingToasts(true);
  }, []);

  const disablePendingToasts = useCallback(() => {
    setShowPendingToasts(false);
  }, []);

  const value = useMemo(
    () => ({
      enablePendingToasts,
      disablePendingToasts,
    }),
    [enablePendingToasts, disablePendingToasts]
  );

  return (
    <GenerationNotificationContext.Provider value={value}>
      {children}
    </GenerationNotificationContext.Provider>
  );
};
