import { useCallback, useMemo, useRef, useState } from 'react';
import { useAtomValue } from 'jotai';
import Fuse from 'fuse.js';

import { autocompleteStateAtom } from 'src/atoms/globalSearch';
import { amplitudeTrackEventAtom } from 'src/atoms/amplitude/amplitudeTrackEventAtom';
import { SpiffyMetricsEventName } from 'src/services/amplitudeService/amplitudeService';
import { SpiffyWidgets } from 'src/application/models/spiffyWidgets';

export interface UseSearchInputProps {
  initialSearchText?: string;
  searchOrigin: SpiffyWidgets;
  onSearchSubmit?: (query: string) => void;
}

export interface UseSearchInputReturn {
  // State
  searchText: string;
  hasText: boolean;
  isFocused: boolean;
  focusedIndex: number;
  focusedOptionId: string | undefined;
  autocompleteResults: string[];
  shouldShowAutocomplete: boolean;

  // Handlers
  handleSearchInputChange: (value: string) => void;
  handleSearchInputFocus: () => void;
  handleSearchInputBlur: () => void;
  handleKeyDown: (event: React.KeyboardEvent<HTMLInputElement>) => void;
  handleAutocompleteSelect: (suggestion: string, rankPosition?: number) => void;
  handleSubmitSearch: () => void;

  // Utilities
  setSearchText: (text: string) => void;
  resetSearch: () => void;
}

export const useSearchInput = ({
  initialSearchText = '',
  searchOrigin,
  onSearchSubmit,
}: UseSearchInputProps): UseSearchInputReturn => {
  const track = useAtomValue(amplitudeTrackEventAtom);

  // State
  const [searchText, setSearchText] = useState(initialSearchText);
  const [hasText, setHasText] = useState(initialSearchText.trim().length > 0);
  const [isFocused, setIsFocused] = useState(false);
  const [focusedIndex, setFocusedIndex] = useState(-1);
  const [focusedOptionId, setFocusedOptionId] = useState<string | undefined>(undefined);
  const autocompleteState = useAtomValue(autocompleteStateAtom);
  const globalAutocompleteResults = autocompleteState.results;
  const isAutocompleteEnabled = globalAutocompleteResults.length > 0;

  // Refs
  const isSelectingAutocomplete = useRef(false);

  // Computed
  const shouldShowAutocomplete = hasText && isFocused && isAutocompleteEnabled;

  const autocompleteResultsLimit = 5;

  // Configure Fuse.js for fuzzy search with custom scoring
  const fuseOptions = useMemo(
    () => ({
      includeScore: true,
      includeMatches: true,
      // Fuzzy matching threshold (0.0 = perfect match, 1.0 = match anything)
      threshold: 0.3,
      // Location where match is expected (0 = beginning)
      location: 0,
      // Distance from location where match can be found - smaller for prefix preference
      distance: 4,
      // Minimum character length before fuzzy matching starts
      minMatchCharLength: 1,
      // Keys to search (we're searching strings directly, so no keys needed)
      keys: [],
      // Use extended search for exact matches and prefix matching
      useExtendedSearch: false,
      // Don't ignore location - this helps prioritize matches at the beginning
      ignoreLocation: false,
    }),
    [],
  );

  const fuse = useMemo(() => {
    if (!globalAutocompleteResults.length) return null;
    return new Fuse(globalAutocompleteResults, fuseOptions);
  }, [globalAutocompleteResults, fuseOptions]);

  // Autocomplete fuzzy search using Fuse.js
  const autocompleteResults = useMemo(() => {
    if (!isAutocompleteEnabled || !fuse) {
      return [];
    }

    if (!searchText.trim()) {
      return [];
    }

    const fuseResults = fuse.search(searchText);

    // Use only Fuse.js scoring (results are already sorted by relevance)
    return fuseResults.slice(0, autocompleteResultsLimit).map(result => result.item);
  }, [isAutocompleteEnabled, fuse, searchText, autocompleteResultsLimit]);

  // Handlers
  const handleSearchInputChange = useCallback(
    (newValue: string) => {
      // If the user is changing text, they must be interacting with the input
      // So we can safely assume they're focused
      if (newValue !== searchText) {
        setIsFocused(true);
      }

      if (newValue.length === 1) {
        track?.({
          eventName: SpiffyMetricsEventName.SearchInputStarted,
          eventProps: {
            searchOrigin,
          },
        });
      }
      setSearchText(newValue);
      setHasText(newValue.trim().length > 0);
      setFocusedIndex(-1);
      setFocusedOptionId(undefined);
    },
    [searchOrigin, searchText, track],
  );

  const handleSearchInputFocus = useCallback(() => {
    setIsFocused(true);
  }, []);

  const handleSearchInputBlur = useCallback(() => {
    setTimeout(() => {
      if (!isSelectingAutocomplete.current) {
        setIsFocused(false);
        setFocusedIndex(-1);
        setFocusedOptionId(undefined);
      }
    }, 150);
  }, []);

  const handleSubmitSearch = useCallback(() => {
    setIsFocused(false);
    if (searchText.trim() && onSearchSubmit) {
      track?.({
        eventName: SpiffyMetricsEventName.SearchQuerySubmitted,
        eventProps: {
          searchOrigin,
          queryText: searchText.trim(),
        },
        alsoSendToGoogleAnalytics: true,
      });
      onSearchSubmit(searchText.trim());
    }
  }, [searchText, onSearchSubmit, searchOrigin, track]);

  const handleAutocompleteSelect = useCallback(
    (suggestion: string, rankPosition?: number) => {
      isSelectingAutocomplete.current = true;
      setSearchText(suggestion);
      setHasText(suggestion.trim().length > 0);
      setIsFocused(false);

      track?.({
        eventName: SpiffyMetricsEventName.SearchInputStarted,
        eventProps: {
          searchOrigin,
        },
      });

      if (onSearchSubmit) {
        onSearchSubmit(suggestion.trim());
      }

      setTimeout(() => {
        isSelectingAutocomplete.current = false;
      }, 100);
    },
    [onSearchSubmit, searchOrigin, track],
  );

  const handleKeyDown = useCallback(
    (event: React.KeyboardEvent<HTMLInputElement>) => {
      if (event.key === 'ArrowDown') {
        event.preventDefault();
        const newIndex = (focusedIndex + 1) % autocompleteResults.length;
        setFocusedIndex(newIndex);
        setFocusedOptionId(`option-${newIndex}`);
      } else if (event.key === 'ArrowUp') {
        event.preventDefault();
        const newIndex =
          (focusedIndex - 1 + autocompleteResults.length) % autocompleteResults.length;
        setFocusedIndex(newIndex);
        setFocusedOptionId(`option-${newIndex}`);
      } else if (event.key === 'Enter') {
        if (focusedIndex === -1) {
          event.preventDefault();
          handleSubmitSearch();
        } else {
          event.preventDefault();
          const suggestionText = autocompleteResults[focusedIndex];
          handleAutocompleteSelect(suggestionText, focusedIndex);
        }
      } else if (event.key === 'Escape') {
        event.preventDefault();
        setFocusedIndex(-1);
        setFocusedOptionId(undefined);
      }
    },
    [autocompleteResults, focusedIndex, handleAutocompleteSelect, handleSubmitSearch],
  );

  const setSearchTextUtility = useCallback((text: string) => {
    setSearchText(text);
    setHasText(text.trim().length > 0);
  }, []);

  const resetSearch = useCallback(() => {
    setSearchText('');
    setHasText(false);
    setIsFocused(false);
    setFocusedIndex(-1);
    setFocusedOptionId(undefined);
  }, []);

  return {
    // State
    searchText,
    hasText,
    isFocused,
    focusedIndex,
    focusedOptionId,
    autocompleteResults,
    shouldShowAutocomplete,

    // Handlers
    handleSearchInputChange,
    handleSearchInputFocus,
    handleSearchInputBlur,
    handleKeyDown,
    handleAutocompleteSelect,
    handleSubmitSearch,

    // Utilities
    setSearchText: setSearchTextUtility,
    resetSearch,
  };
};
