import { useEffect, useRef, useState } from "preact/hooks";
import { Search } from "lucide-preact";
import { AuthScreen } from "@/components/auth/auth-screen";
import { SearchResultsPanel } from "@/components/search/search-results-panel";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { createReusableSearch, getSearch, runSearchStream, type SearchDetail } from "@/lib/api";
import { useSessionAuth } from "@/hooks/use-session-auth";

function readQueryFromUrl() {
  const params = new URLSearchParams(window.location.search);
  return params.get("q")?.trim() ?? "";
}

function pushSearchQuery(query: string) {
  const params = new URLSearchParams(window.location.search);
  if (query.trim()) {
    params.set("q", query.trim());
  } else {
    params.delete("q");
  }
  const next = `${window.location.pathname}${params.toString() ? `?${params.toString()}` : ""}`;
  window.history.pushState({}, "", next);
}

function isInterruptedStreamError(message: string | null | undefined) {
  if (!message) return false;
  const normalized = message.toLowerCase();
  return (
    normalized.includes("abort") ||
    normalized.includes("interrupted") ||
    normalized.includes("cancelled") ||
    normalized.includes("canceled") ||
    normalized.includes("load failed") ||
    normalized.includes("failed to fetch")
  );
}

export default function SearchRoutePage() {
  const {
    authTokenInput,
    setAuthTokenInput,
    isCheckingSession,
    isSigningIn,
    isAuthenticated,
    authError,
    handleAuthFailure,
    handleSignIn,
  } = useSessionAuth();

  const [queryInput, setQueryInput] = useState(readQueryFromUrl());
  const [routeQuery, setRouteQuery] = useState(readQueryFromUrl());
  const [search, setSearch] = useState<SearchDetail | null>(null);
  const [isRunning, setIsRunning] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const requestCounterRef = useRef(0);
  const streamAbortRef = useRef<AbortController | null>(null);

  useEffect(() => {
    const onPopState = () => {
      const next = readQueryFromUrl();
      setRouteQuery(next);
      setQueryInput(next);
    };
    window.addEventListener("popstate", onPopState);
    return () => window.removeEventListener("popstate", onPopState);
  }, []);

  useEffect(() => {
    document.title = routeQuery ? `${routeQuery} — Sybil` : "Sybil";
  }, [routeQuery]);

  useEffect(() => {
    return () => {
      document.title = "Sybil";
    };
  }, []);

  useEffect(() => {
    return () => {
      streamAbortRef.current?.abort();
      streamAbortRef.current = null;
    };
  }, []);

  const runQuery = async (query: string) => {
    const trimmed = query.trim();
    const requestId = ++requestCounterRef.current;
    streamAbortRef.current?.abort();

    if (!trimmed) {
      setSearch(null);
      setError(null);
      setIsRunning(false);
      return;
    }

    const abortController = new AbortController();
    streamAbortRef.current = abortController;
    let wasInterrupted = false;
    setError(null);
    setIsRunning(true);

    const nowIso = new Date().toISOString();
    setSearch({
      id: `temp-search-${requestId}`,
      title: trimmed.slice(0, 80),
      query: trimmed,
      createdAt: nowIso,
      updatedAt: nowIso,
      starred: false,
      starredAt: null,
      requestId: null,
      latencyMs: null,
      error: null,
      answerText: null,
      answerRequestId: null,
      answerCitations: null,
      answerError: null,
      results: [],
    });

    try {
      const createdResult = await createReusableSearch({
        query: trimmed,
        title: trimmed.slice(0, 80),
      });
      const created = createdResult.search;
      if (requestId !== requestCounterRef.current) return;

      setSearch((current) =>
        current
          ? {
              ...current,
              id: created.id,
              title: created.title,
              query: created.query,
              createdAt: created.createdAt,
              updatedAt: created.updatedAt,
              starred: created.starred,
              starredAt: created.starredAt,
            }
          : current
      );

      if (createdResult.cacheHit) {
        const cached = await getSearch(created.id);
        if (requestId !== requestCounterRef.current) return;
        setSearch(cached);
        return;
      }

      await runSearchStream(
        created.id,
        {
          query: trimmed,
          title: trimmed.slice(0, 80),
          type: "auto",
          numResults: 10,
        },
        {
          onSearchResults: (payload) => {
            if (requestId !== requestCounterRef.current) return;
            setSearch((current) =>
              current
                ? {
                    ...current,
                    requestId: payload.requestId ?? current.requestId,
                    error: null,
                    results: payload.results,
                  }
                : current
            );
          },
          onSearchError: (payload) => {
            if (requestId !== requestCounterRef.current) return;
            setSearch((current) => (current ? { ...current, error: payload.error } : current));
          },
          onAnswer: (payload) => {
            if (requestId !== requestCounterRef.current) return;
            setSearch((current) =>
              current
                ? {
                    ...current,
                    answerText: payload.answerText,
                    answerRequestId: payload.answerRequestId,
                    answerCitations: payload.answerCitations,
                    answerError: null,
                  }
                : current
            );
          },
          onAnswerError: (payload) => {
            if (requestId !== requestCounterRef.current) return;
            if (isInterruptedStreamError(payload.error)) {
              wasInterrupted = true;
              return;
            }
            setSearch((current) => (current ? { ...current, answerError: payload.error } : current));
          },
          onDone: (payload) => {
            if (requestId !== requestCounterRef.current) return;
            setSearch(payload.search);
          },
          onError: (payload) => {
            if (requestId !== requestCounterRef.current) return;
            if (isInterruptedStreamError(payload.message)) {
              wasInterrupted = true;
              return;
            }
            setError(payload.message);
          },
        },
        { signal: abortController.signal }
      );
    } catch (err) {
      if (abortController.signal.aborted) {
        wasInterrupted = true;
        return;
      }
      const message = err instanceof Error ? err.message : String(err);
      if (isInterruptedStreamError(message)) {
        wasInterrupted = true;
        return;
      }
      if (message.includes("bearer token")) {
        handleAuthFailure(message);
      } else if (requestId === requestCounterRef.current) {
        setError(message);
      }
    } finally {
      if (requestId === requestCounterRef.current && !wasInterrupted) {
        streamAbortRef.current = null;
        setIsRunning(false);
      }
    }
  };

  useEffect(() => {
    if (!isAuthenticated) return;
    void runQuery(routeQuery);
  }, [isAuthenticated, routeQuery]);

  if (isCheckingSession) {
    return (
      <div className="flex h-full items-center justify-center">
        <p className="text-sm text-muted-foreground">Checking session...</p>
      </div>
    );
  }

  if (!isAuthenticated) {
    return (
      <AuthScreen
        authTokenInput={authTokenInput}
        setAuthTokenInput={setAuthTokenInput}
        isSigningIn={isSigningIn}
        authError={authError}
        onSignIn={handleSignIn}
      />
    );
  }

  return (
    <div className="app-search-safe-pad h-full overflow-y-auto">
      <div className="mx-auto w-full max-w-4xl space-y-5">
        <form
          className="flex items-center gap-2 rounded-xl border bg-background p-2 shadow-sm"
          onSubmit={(event) => {
            event.preventDefault();
            const next = queryInput.trim();
            pushSearchQuery(next);
            setRouteQuery(next);
          }}
        >
          <Input value={queryInput} onInput={(event) => setQueryInput(event.currentTarget.value)} placeholder="Search the web" />
          <Button type="submit" size="icon" disabled={!queryInput.trim() || isRunning}>
            <Search className="h-4 w-4" />
          </Button>
        </form>

        {error ? <p className="text-sm text-red-600">{error}</p> : null}

        <SearchResultsPanel
          search={search}
          isLoading={false}
          isRunning={isRunning}
          className="w-full"
          enableKeyboardNavigation
          openLinksInNewTab={false}
        />
      </div>
    </div>
  );
}
