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;
}
