import { useCallback, useMemo } from 'react';
import { useSidebarStore } from '@/store/sidebarStore';
import { useReaderStore } from '@/store/readerStore';
import { useBookProgress } from '@/store/readerProgressStore';
import { createCfiLocationMatcher } from '@/utils/cfi';
import { flattenSearchResults } from '../components/sidebar/SearchResultsNav';

export function useSearchNav(bookKey: string) {
  const getView = useReaderStore((s) => s.getView);
  const { setSideBarVisible, setSearchBarVisible } = useSidebarStore();
  const { getSearchNavState, setSearchResultIndex, clearSearch } = useSidebarStore();

  const searchNavState = getSearchNavState(bookKey);
  const { searchTerm, searchResults, searchResultIndex, searchProgress } = searchNavState;

  // Reactive: search nav re-derives current-page boundaries when the user
  // turns the page. Subscribes to readerProgressStore only.
  const progress = useBookProgress(bookKey);

  const currentLocation = useMemo(() => {
    return progress?.location;
  }, [progress]);

  // Flatten search results for navigation
  const flattenedResults = useMemo(() => {
    if (!searchResults) return [];
    return flattenSearchResults(searchResults);
  }, [searchResults]);

  const totalResults = flattenedResults.length;
  const hasSearchResults = searchResults && totalResults > 0;
  const showSearchNav = hasSearchResults;

  // Get current section label
  const currentSection = useMemo(() => {
    if (!flattenedResults.length || searchResultIndex >= flattenedResults.length) return '';
    return flattenedResults[searchResultIndex]?.sectionLabel || '';
  }, [flattenedResults, searchResultIndex]);

  // Find results on the current page.
  // Uses a batched CFI matcher so the location is collapsed only once per
  // page turn instead of once per search hit — see createCfiLocationMatcher
  // in utils/cfi for the why.
  const currentPageResults = useMemo(() => {
    if (!flattenedResults.length || !currentLocation) return { firstIndex: -1, lastIndex: -1 };

    const matches = createCfiLocationMatcher(currentLocation);
    let firstIndex = -1;
    let lastIndex = -1;

    for (let i = 0; i < flattenedResults.length; i++) {
      const result = flattenedResults[i];
      if (result && matches(result.cfi)) {
        if (firstIndex === -1) firstIndex = i;
        lastIndex = i;
      }
    }
    if (firstIndex !== -1) {
      setTimeout(() => setSearchResultIndex(bookKey, firstIndex), 0);
    }

    return { firstIndex, lastIndex };
  }, [flattenedResults, currentLocation, bookKey, setSearchResultIndex]);

  // Navigate to a specific search result
  const navigateToResult = useCallback(
    (index: number) => {
      if (!flattenedResults.length) return;
      if (index < 0 || index >= flattenedResults.length) return;

      const result = flattenedResults[index];
      if (result) {
        setSearchResultIndex(bookKey, index);
        getView(bookKey)?.goTo(result.cfi);
      }
    },
    [bookKey, flattenedResults, setSearchResultIndex, getView],
  );

  const handleShowResults = useCallback(() => {
    setSideBarVisible(true);
  }, [setSideBarVisible]);

  const handleCloseSearch = useCallback(() => {
    clearSearch(bookKey);
    // Exit the sidebar's search mode too, not just the results — otherwise
    // reopening the sidebar still shows the (empty) search bar.
    setSearchBarVisible(false);
    getView(bookKey)?.clearSearch();
  }, [clearSearch, bookKey, getView, setSearchBarVisible]);

  // Navigate to the previous page with results (last result before current page)
  const handlePreviousResult = useCallback(() => {
    const { firstIndex } = currentPageResults;

    if (firstIndex > 0) {
      // Navigate to the result just before the first result on current page
      navigateToResult(firstIndex - 1);
    } else if (firstIndex === -1 && searchResultIndex > 0) {
      // No results on current page, just go to previous result
      navigateToResult(searchResultIndex - 1);
    }
  }, [currentPageResults, searchResultIndex, navigateToResult]);

  // Navigate to the next page with results (first result after current page)
  const handleNextResult = useCallback(() => {
    const { lastIndex } = currentPageResults;

    if (lastIndex >= 0 && lastIndex < totalResults - 1) {
      // Navigate to the result just after the last result on current page
      navigateToResult(lastIndex + 1);
    } else if (lastIndex === -1 && searchResultIndex < totalResults - 1) {
      // No results on current page, just go to next result
      navigateToResult(searchResultIndex + 1);
    }
  }, [currentPageResults, totalResults, searchResultIndex, navigateToResult]);

  // Check if there are results before/after the current page
  const hasPreviousPage =
    currentPageResults.firstIndex > 0 ||
    (currentPageResults.firstIndex === -1 && searchResultIndex > 0);
  const hasNextPage =
    (currentPageResults.lastIndex >= 0 && currentPageResults.lastIndex < totalResults - 1) ||
    (currentPageResults.lastIndex === -1 && searchResultIndex < totalResults - 1);

  return {
    searchTerm,
    searchProgress,
    currentSection,
    searchResultIndex,
    totalResults,
    showSearchNav,
    hasPreviousPage,
    hasNextPage,
    handleShowResults,
    handleCloseSearch,
    handlePreviousResult,
    handleNextResult,
  };
}
