import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
import type { ArtistCreditMode } from '@/lib/api/library';
import { useEffect, useRef, useState } from 'react';
import {
  BROWSE_TEXT_DEBOUNCE_NETWORK_MS,
  BROWSE_TEXT_DEBOUNCE_RACE_MS,
  browseRaceCountsArtists,
  raceBrowseWithLocalFallback,
  runLocalBrowseArtists,
  runNetworkBrowseArtists,
  type LibrarySearchSurface,
} from '@/lib/library/browseTextSearch';
import { useOfflineBrowseContext } from '@/features/offline';
import { offlineLocalBrowseEnabled, searchOfflineLocalArtists } from '@/features/offline';
import {
  artistBrowseTimed,
  emitArtistsBrowseDebug,
} from '@/lib/library/artistBrowseDebug';

/**
 * Debounced artist/composer name search with local-vs-network race when the
 * library index is enabled. Returns `textSearchArtists` when a raced query is
 * active; callers should pass `effectiveFilter` (empty while raced) into their
 * local filter hook so the query is not applied twice.
 */
export function useBrowseArtistTextSearch(
  filter: string,
  indexEnabled: boolean,
  serverId: string | null | undefined,
  surface: LibrarySearchSurface = 'artists_browse',
  creditMode: ArtistCreditMode = 'album',
  starredOnly = false,
) {
  const offlineBrowseActive = useOfflineBrowseContext().active;
  const [debouncedFilter, setDebouncedFilter] = useState('');
  const [textSearchArtists, setTextSearchArtists] = useState<SubsonicArtist[] | null>(null);
  const [textSearchLoading, setTextSearchLoading] = useState(false);
  const searchGenRef = useRef(0);

  useEffect(() => {
    const ms = indexEnabled ? BROWSE_TEXT_DEBOUNCE_RACE_MS : BROWSE_TEXT_DEBOUNCE_NETWORK_MS;
    const timer = window.setTimeout(() => setDebouncedFilter(filter.trim()), ms);
    return () => window.clearTimeout(timer);
  }, [filter, indexEnabled]);

  useEffect(() => {
    const q = debouncedFilter;
    if (starredOnly || !q || !indexEnabled || !serverId) {
      // React Compiler set-state-in-effect rule: state set from a timer/animation callback.
      // eslint-disable-next-line react-hooks/set-state-in-effect
      setTextSearchArtists(null);
      setTextSearchLoading(false);
      return;
    }

    const gen = ++searchGenRef.current;
    const isStale = () => gen !== searchGenRef.current;
    setTextSearchLoading(true);
    emitArtistsBrowseDebug('text_search_start', { query: q, creditMode, surface });

    void (async () => {
      if (offlineBrowseActive) {
        const artists = offlineLocalBrowseEnabled(serverId)
          ? await artistBrowseTimed(
            'text_search_offline',
            () => searchOfflineLocalArtists(serverId, q, creditMode),
            { query: q },
          )
          : [];
        if (isStale()) return;
        setTextSearchArtists(artists);
        setTextSearchLoading(false);
        emitArtistsBrowseDebug('text_search_done', { source: 'offline', artistCount: artists?.length ?? 0 });
        return;
      }
      const outcome = await artistBrowseTimed(
        'text_search_race',
        () => raceBrowseWithLocalFallback(
          isStale,
          () => runLocalBrowseArtists(serverId, q, creditMode),
          () => runNetworkBrowseArtists(q, creditMode),
          {
            surface,
            query: q,
            indexEnabled,
            counts: browseRaceCountsArtists,
          },
        ),
        { query: q, creditMode },
      );
      if (isStale()) return;
      setTextSearchArtists(outcome?.result ?? null);
      setTextSearchLoading(false);
      emitArtistsBrowseDebug('text_search_done', {
        source: outcome?.source ?? 'none',
        artistCount: outcome?.result?.length ?? 0,
      });
    })();
  }, [creditMode, debouncedFilter, indexEnabled, offlineBrowseActive, serverId, starredOnly, surface]);

  const effectiveFilter = textSearchArtists != null ? '' : filter;
  return { textSearchArtists, textSearchLoading, effectiveFilter };
}
