import { useEffect, useState, useRef } from 'react';
import { useSearch } from '@embedpdf/plugin-search/react';
import { useViewer } from '@app/contexts/ViewerContext';
import { useActiveDocumentId } from '@app/components/viewer/useActiveDocumentId';
import { useDocumentReady } from '@app/components/viewer/hooks/useDocumentReady';

interface SearchResult {
  pageIndex: number;
  rects: Array<{
    origin: { x: number; y: number };
    size: { width: number; height: number };
  }>;
}

/**
 * SearchAPIBridge - Updated for embedPDF v2.6.0
 * Connects the PDF search plugin to the shared ViewerContext.
 */
export function SearchAPIBridge() {
  const activeDocumentId = useActiveDocumentId();
  const documentReady = useDocumentReady();

  // Don't render the inner component until we have a valid document ID and document is ready
  if (!activeDocumentId || !documentReady) {
    return null;
  }

  return <SearchAPIBridgeInner documentId={activeDocumentId} />;
}

function SearchAPIBridgeInner({ documentId }: { documentId: string }) {
  const { provides: search } = useSearch(documentId);
  const { registerBridge, scrollActions } = useViewer();

  // Keep search ref updated to avoid re-running effects when object reference changes
  const searchRef = useRef(search);
  const isSearchingRef = useRef(false);
  const lastScrolledIndexRef = useRef<number | null>(null);

  useEffect(() => {
    searchRef.current = search;
  }, [search]);

  const [localState, setLocalState] = useState({
    results: null as SearchResult[] | null,
    activeIndex: 0
  });

  // Subscribe to search result changes from EmbedPDF
  const subscriptionRef = useRef<(() => void) | null>(null);

  useEffect(() => {
    // Cleanup previous subscription
    if (subscriptionRef.current) {
      subscriptionRef.current();
      subscriptionRef.current = null;
    }

    if (!search) return;

    subscriptionRef.current = search.onSearchResultStateChange?.((state: any) => {
      if (!state) return;

      const newState = {
        results: state.results || null,
        activeIndex: (state.activeResultIndex || 0) + 1 // Convert to 1-based index
      };

      setLocalState(prevState => {
        // Only update if state actually changed
        if (prevState.results !== newState.results || prevState.activeIndex !== newState.activeIndex) {
          return newState;
        }
        return prevState;
      });
    }) ?? null;

    return () => {
      if (subscriptionRef.current) {
        subscriptionRef.current();
        subscriptionRef.current = null;
      }
    };
  }, [search]);

  // Extract primitive values from localState to avoid object reference dependencies
  const localResults = localState.results;
  const localActiveIndex = localState.activeIndex;

  // Scroll to active result when it changes (for next/previous navigation)
  useEffect(() => {
    if (!localResults || localResults.length === 0) {
      lastScrolledIndexRef.current = null;
      return;
    }

    // Only scroll if the active index actually changed
    const activeResultIndex = localActiveIndex - 1; // Convert back to 0-based
    if (activeResultIndex >= 0 &&
        activeResultIndex < localResults.length &&
        lastScrolledIndexRef.current !== activeResultIndex) {
      const activeResult = localResults[activeResultIndex];
      if (activeResult) {
        const pageNumber = activeResult.pageIndex + 1; // Convert to 1-based page number
        scrollActions.scrollToPage(pageNumber);
        lastScrolledIndexRef.current = activeResultIndex;
      }
    }
  }, [localResults, localActiveIndex, scrollActions]);

  // Register bridge whenever state changes
  useEffect(() => {
    const currentSearch = searchRef.current;
    if (currentSearch) {
      registerBridge('search', {
        state: { results: localResults, activeIndex: localActiveIndex },
        api: {
          search: async (query: string) => {
            // Prevent overlapping searches
            if (isSearchingRef.current) {
              return null;
            }

            if (!currentSearch?.startSearch || !currentSearch?.searchAllPages) {
              return null;
            }

            isSearchingRef.current = true;

            try {
              currentSearch.startSearch();
              const results = await currentSearch.searchAllPages(query);
              return results;
            } catch (error: any) {
              // Handle abort errors gracefully - these occur when searches overlap
              if (error?.type === 'abort' || error?.message?.includes('abort')) {
                // Silently handle abort - this is expected when user types quickly
                return null;
              }
              console.error('Search failed:', error);
              return null;
            } finally {
              isSearchingRef.current = false;
            }
          },
          clear: () => {
            try {
              if (currentSearch?.stopSearch) {
                currentSearch.stopSearch();
              }
            } catch (error) {
              console.warn('Error stopping search:', error);
            }
            setLocalState({ results: null, activeIndex: 0 });
          },
          next: () => {
            try {
              currentSearch?.nextResult?.();
            } catch (error) {
              console.warn('Error navigating to next result:', error);
            }
          },
          previous: () => {
            try {
              currentSearch?.previousResult?.();
            } catch (error) {
              console.warn('Error navigating to previous result:', error);
            }
          },
          goToResult: (index: number) => {
            try {
              currentSearch?.goToResult?.(index);
            } catch (error) {
              console.warn('Error going to result:', error);
            }
          },
        }
      });
    }

    return () => {
      registerBridge('search', null);
    };
  }, [localResults, localActiveIndex, registerBridge]);

  return null;
}
