import { useEffect, useRef, useState } from "preact/hooks";
import type { SearchDetail } from "@/lib/api";
import { MarkdownContent } from "@/components/markdown/markdown-content";
import { cn } from "@/lib/utils";
import { MessageSquare } from "lucide-preact";

function formatHost(url: string) {
  try {
    return new URL(url).hostname.replace(/^www\./, "");
  } catch {
    return url;
  }
}

function normalizeHref(href: string) {
  try {
    const parsed = new URL(href);
    parsed.hash = "";
    const normalized = parsed.toString();
    return normalized.endsWith("/") ? normalized.slice(0, -1) : normalized;
  } catch {
    return href.trim().replace(/\/$/, "");
  }
}

type Props = {
  search: SearchDetail | null;
  isLoading: boolean;
  isRunning: boolean;
  className?: string;
  enableKeyboardNavigation?: boolean;
  openLinksInNewTab?: boolean;
  isStartingChat?: boolean;
  onStartChat?: () => void;
};

export function SearchResultsPanel({
  search,
  isLoading,
  isRunning,
  className,
  enableKeyboardNavigation = false,
  openLinksInNewTab = true,
  isStartingChat = false,
  onStartChat,
}: Props) {
  const ANSWER_COLLAPSED_HEIGHT_CLASS = "h-[3rem]";
  const [isAnswerExpanded, setIsAnswerExpanded] = useState(false);
  const [canExpandAnswer, setCanExpandAnswer] = useState(false);
  const [activeResultIndex, setActiveResultIndex] = useState(-1);
  const answerBodyRef = useRef<HTMLDivElement | null>(null);

  useEffect(() => {
    setIsAnswerExpanded(false);
    setCanExpandAnswer(false);
  }, [search?.id, search?.answerText]);

  useEffect(() => {
    const el = answerBodyRef.current;
    if (!el || !search?.answerText) {
      setCanExpandAnswer(false);
      return;
    }
    if (isAnswerExpanded) {
      setCanExpandAnswer(true);
      return;
    }
    setCanExpandAnswer(el.scrollHeight - el.clientHeight > 1);
  }, [search?.answerText, isAnswerExpanded]);

  useEffect(() => {
    setActiveResultIndex(search?.results.length ? 0 : -1);
  }, [search?.id, search?.results.length]);

  useEffect(() => {
    if (!enableKeyboardNavigation) return;

    const onKeyDown = (event: KeyboardEvent) => {
      if (!search?.results.length || event.metaKey || event.ctrlKey || event.altKey) return;

      const target = event.target;
      if (
        target instanceof HTMLInputElement ||
        target instanceof HTMLTextAreaElement ||
        target instanceof HTMLSelectElement ||
        (target instanceof HTMLElement && target.isContentEditable)
      ) {
        return;
      }

      if (event.key === "ArrowLeft") {
        event.preventDefault();
        setActiveResultIndex((current) => Math.max(0, (current < 0 ? 0 : current) - 1));
        return;
      }

      if (event.key === "ArrowRight") {
        event.preventDefault();
        setActiveResultIndex((current) => Math.min(search.results.length - 1, current + 1));
        return;
      }

      if (event.key !== "Enter") return;

      const result = search.results[activeResultIndex >= 0 ? activeResultIndex : 0];
      if (!result?.url) return;
      event.preventDefault();
      if (openLinksInNewTab) {
        window.open(result.url, "_blank", "noopener,noreferrer");
      } else {
        window.location.assign(result.url);
      }
    };

    window.addEventListener("keydown", onKeyDown);
    return () => window.removeEventListener("keydown", onKeyDown);
  }, [activeResultIndex, enableKeyboardNavigation, openLinksInNewTab, search]);

  const citationEntries = (search?.answerCitations ?? [])
    .map((citation, index) => {
      const href = citation.url || citation.id || "";
      if (!href) return null;
      return {
        href,
        normalizedHref: normalizeHref(href),
        index: index + 1,
        label: citation.title?.trim() || formatHost(href),
      };
    })
    .filter((entry): entry is { href: string; normalizedHref: string; index: number; label: string } => !!entry);

  const resolveCitationIndex = (href: string) => {
    const normalized = normalizeHref(href);
    return citationEntries.find((entry) => entry.normalizedHref === normalized)?.index;
  };

  const hasAnswerText = !!search?.answerText;
  const isAnswerLoading = isRunning && !hasAnswerText;
  const hasCitations = citationEntries.length > 0;
  const isExpandable = hasAnswerText && (canExpandAnswer || hasCitations);
  const canStartChat = !!search && !isLoading && !isRunning && !isStartingChat && (!!search.answerText || search.results.length > 0);

  return (
    <div className={className ?? "mx-auto w-full max-w-4xl"}>
      {search?.query ? (
        <div className="mb-5 flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
          <div className="min-w-0">
            <p className="text-sm text-muted-foreground">Results for</p>
            <h2 className="mt-1 break-words text-xl font-semibold text-violet-50">{search.query}</h2>
            <p className="mt-1 text-xs text-muted-foreground">
              {search.results.length} result{search.results.length === 1 ? "" : "s"}
              {search.latencyMs ? ` • ${search.latencyMs} ms` : ""}
            </p>
          </div>
          {onStartChat ? (
            <button
              type="button"
              className="inline-flex h-10 shrink-0 items-center justify-center gap-2 rounded-lg border border-violet-300/24 bg-violet-300/10 px-3 text-sm font-medium text-violet-50 transition hover:bg-violet-300/16 disabled:cursor-not-allowed disabled:opacity-50"
              onClick={onStartChat}
              disabled={!canStartChat}
            >
              <MessageSquare className="h-4 w-4" />
              {isStartingChat ? "Starting chat..." : "Chat with results"}
            </button>
          ) : null}
        </div>
      ) : null}

      {(isRunning || !!search?.answerText || !!search?.answerError) && (
        <section className="mb-6 rounded-xl border border-violet-300/24 bg-[linear-gradient(135deg,hsl(240_46%_8%_/_0.94),hsl(260_40%_12%_/_0.88))] p-4 shadow-[inset_0_1px_0_hsl(255_100%_92%_/_0.06)]">
          <p className="text-xs font-semibold uppercase text-violet-300/90">Answer</p>
          {(isAnswerLoading || hasAnswerText) ? (
            <div className="mt-2">
              <div className="relative">
                <div
                  ref={answerBodyRef}
                  className={cn(
                    "overflow-hidden",
                    !isAnswerExpanded && ANSWER_COLLAPSED_HEIGHT_CLASS
                  )}
                >
                  {isAnswerLoading ? (
                    <div className="text-sm text-muted-foreground">Generating answer...</div>
                  ) : (
                    <MarkdownContent
                      markdown={search?.answerText ?? ""}
                      mode="citationTokens"
                      resolveCitationIndex={resolveCitationIndex}
                      className="text-sm leading-6 text-violet-50"
                    />
                  )}
                </div>
                {!isAnswerExpanded && (isExpandable || isAnswerLoading) ? (
                  <div className="pointer-events-none absolute inset-x-0 bottom-0 h-7 bg-gradient-to-t from-[hsl(252_42%_10%)] to-transparent" />
                ) : null}
              </div>
              <div className="mt-2 h-5">
                {isExpandable ? (
                  <button
                    type="button"
                    className="inline-flex items-center gap-1 text-xs font-medium text-violet-200 hover:text-violet-100"
                    onClick={() => setIsAnswerExpanded((current) => !current)}
                  >
                    <span className={cn("inline-block text-[11px] transition-transform", isAnswerExpanded && "rotate-180")}>▾</span>
                    {isAnswerExpanded ? "Show less" : "Show more"}
                  </button>
                ) : null}
              </div>
            </div>
          ) : null}
          {search?.answerError ? <p className="mt-2 text-sm text-red-500">{search.answerError}</p> : null}
          {isAnswerExpanded && !!citationEntries.length && (
            <div className="mt-3 flex flex-wrap gap-2">
              {citationEntries.slice(0, 8).map((citation) => {
                return (
                  <a
                    key={`${citation.href}-${citation.index}`}
                    href={citation.href}
                    target={openLinksInNewTab ? "_blank" : undefined}
                    rel={openLinksInNewTab ? "noreferrer" : undefined}
                    className="max-w-full truncate rounded-md border border-violet-300/28 bg-violet-300/8 px-2 py-1 text-xs text-violet-200 hover:bg-violet-500/20"
                  >
                    <span className="mr-1 rounded bg-violet-900/70 px-1 py-0.5 text-[10px] text-violet-100">{citation.index}</span>
                    {citation.label}
                  </a>
                );
              })}
            </div>
          )}
        </section>
      )}

      {(isLoading || isRunning) && !search?.results.length ? (
        <p className="text-sm text-muted-foreground">{isRunning ? "Searching Exa..." : "Loading search..."}</p>
      ) : null}

      {!isLoading && !isRunning && !!search?.query && search.results.length === 0 ? (
        <p className="text-sm text-muted-foreground">No results found.</p>
      ) : null}

      <div className="space-y-6">
        {search?.results.map((result, index) => {
          return (
            <article
              key={result.id}
              className={cn(
                "rounded-lg border border-violet-300/16 bg-[linear-gradient(135deg,hsl(238_44%_7%_/_0.92),hsl(250_34%_10%_/_0.86))] px-4 py-4 shadow-sm transition-colors",
                index === activeResultIndex && "border-violet-300/55 ring-1 ring-violet-300/70"
              )}
            >
              <p className="truncate text-xs text-cyan-200/85">{formatHost(result.url)}</p>
              <a
                href={result.url}
                target={openLinksInNewTab ? "_blank" : undefined}
                rel={openLinksInNewTab ? "noreferrer" : undefined}
                className="mt-1 block break-words text-lg font-medium text-violet-200 hover:underline"
              >
                {result.title || result.url}
              </a>
              {(result.publishedDate || result.author) && (
                <p className="mt-1 text-xs text-muted-foreground">{[result.publishedDate, result.author].filter(Boolean).join(" • ")}</p>
              )}
              {result.url ? <p className="mt-2 break-all text-sm leading-6 text-violet-100/82">{result.url}</p> : null}
            </article>
          );
        })}
      </div>

      {search?.error ? <p className="mt-4 text-sm text-red-600">{search.error}</p> : null}
    </div>
  );
}
