import { useCallback, useEffect, useRef, useState } from 'react';
import { useQuery, type UseQueryResult } from '@tanstack/react-query';
import { fetchConversations, fetchThread, searchMessages } from '@/api/messages';
import type { Conversation, Message, MessageSearchHit, SearchMode } from '@/types';
import { getFromCache, saveToCache } from '@/lib/searchCache';
import { STALE_TIME } from './refreshConfig';

export function useConversations(deviceId?: string): UseQueryResult<Conversation[]> {
  return useQuery({
    queryKey: ['conversations', deviceId],
    queryFn: () => fetchConversations(deviceId),
    staleTime: STALE_TIME,
  });
}

export function useThread(conversationId: string | null): UseQueryResult<Message[]> {
  return useQuery({
    queryKey: ['thread', conversationId],
    queryFn: () => fetchThread(conversationId!),
    enabled: conversationId !== null,
    staleTime: STALE_TIME,
  });
}

export interface MessageSearchState {
  hits: MessageSearchHit[];
  total: number;
  hasMore: boolean;
  loadMore: () => Promise<void>;
  isLoading: boolean;
  isLoadingMore: boolean;
  error: Error | null;
}

export function useMessageSearch(
  query: string,
  opts: { conversationId?: string; limit?: number; mode?: SearchMode } = {},
): MessageSearchState {
  const mode: SearchMode = opts.mode ?? 'hybrid';
  const limit = opts.limit ?? 50;
  const trimmed = query.trim();
  const enabled = trimmed.length >= 2;

  const [hits, setHits] = useState<MessageSearchHit[]>(() =>
    enabled ? (getFromCache(trimmed, mode)?.hits ?? []) : [],
  );
  const [total, setTotal] = useState<number>(() =>
    enabled ? (getFromCache(trimmed, mode)?.total ?? 0) : 0,
  );
  const [isLoadingMore, setIsLoadingMore] = useState(false);

  // Reset accumulated state when query/mode/conversationId changes
  const searchKey = `${trimmed}|${mode}|${opts.conversationId ?? ''}`;
  const searchKeyRef = useRef(searchKey);
  if (searchKeyRef.current !== searchKey) {
    searchKeyRef.current = searchKey;
    const cached = enabled ? getFromCache(trimmed, mode) : null;
    setHits(cached?.hits ?? []);
    setTotal(cached?.total ?? 0);
    setIsLoadingMore(false);
  }

  const { data, isLoading, error } = useQuery({
    queryKey: ['messages', 'search', trimmed, mode, opts.conversationId ?? null],
    queryFn: async () => {
      const cached = getFromCache(trimmed, mode);
      // Hybrid always re-ranks the full corpus; lexical/semantic fetch incrementally
      const since = mode !== 'hybrid' && cached ? cached.searchDate : undefined;
      return searchMessages(trimmed, {
        ...(opts.conversationId ? { conversationId: opts.conversationId } : {}),
        limit,
        mode,
        ...(since ? { since } : {}),
      });
    },
    enabled,
    staleTime: STALE_TIME,
  });

  useEffect(() => {
    if (!data) return;
    setHits((prev) => {
      const cached = getFromCache(trimmed, mode);
      const base = cached?.hits ?? prev;

      let merged: MessageSearchHit[];
      if (mode === 'hybrid') {
        merged = [...data.results];
      } else {
        const existingIds = new Set(base.map((h) => h.message.id));
        const newHits = data.results.filter((h) => !existingIds.has(h.message.id));
        merged = newHits.length > 0 ? [...newHits, ...base] : base;
      }

      saveToCache(trimmed, mode, {
        query: trimmed,
        mode,
        hits: merged,
        total: data.total,
        searchDate: data.searchDate,
      });
      return merged;
    });
    setTotal(data.total);
  }, [data, trimmed, mode]);

  const hasMore = hits.length < total;

  const loadMore = useCallback(async () => {
    if (isLoadingMore || !hasMore) return;
    setIsLoadingMore(true);
    try {
      const resp = await searchMessages(trimmed, {
        ...(opts.conversationId ? { conversationId: opts.conversationId } : {}),
        limit,
        mode,
        offset: hits.length,
      });
      setHits((prev) => {
        const existingIds = new Set(prev.map((h) => h.message.id));
        const newHits = resp.results.filter((h) => !existingIds.has(h.message.id));
        const merged = [...prev, ...newHits];
        saveToCache(trimmed, mode, {
          query: trimmed,
          mode,
          hits: merged,
          total: resp.total,
          searchDate: resp.searchDate,
        });
        return merged;
      });
      setTotal(resp.total);
    } finally {
      setIsLoadingMore(false);
    }
  }, [isLoadingMore, hasMore, hits.length, trimmed, mode, opts.conversationId, limit]);

  return {
    hits,
    total,
    hasMore,
    loadMore,
    isLoading: isLoading && hits.length === 0,
    isLoadingMore,
    error: error as Error | null,
  };
}
