"use client";

import { useState, useRef, useCallback, useEffect } from "react";
import { useRouter } from "next/navigation";
import Image from "next/image";
import { motion, AnimatePresence } from "framer-motion";
import { IoSearchOutline, IoCloseOutline } from "react-icons/io5";
import { IconButton } from "@/components/IconButton";
import { useDebounced } from "@/hooks/useDebounced";
import { useClickOutside } from "@/hooks/useClickOutside";
import { ARTIST_PLACEHOLDER_SRC } from "@/lib/constants";

/* ── Types ── */

type SearchResults = {
  releases: Array<{ name: string; albumArtist?: string; slug: string; imageUrl?: string }>;
  artists: Array<{ name: string; role?: string; slug: string; imageUrl?: string }>;
  composers: Array<{ name: string; years?: string; slug: string; imageUrl?: string }>;
  works: Array<{
    title: string;
    composerName?: string;
    arrangerName?: string;
    slug: string;
    imageUrl?: string;
  }>;
  blog: Array<{ title: string; category?: string; slug: string; imageUrl?: string }>;
};

const emptyResults: SearchResults = {
  releases: [],
  artists: [],
  composers: [],
  works: [],
  blog: [],
};

type ResultItem = { title: string; subtitle?: string; href: string; imageUrl?: string };
type ResultGroup = { label: string; items: ResultItem[] };

/* ── Animation ── */

const fadeTransition = { type: "tween" as const, ease: "easeInOut" as const, duration: 0.3 };

/* ── Grouping ── */

function groupResults(data: SearchResults): ResultGroup[] {
  const groups: ResultGroup[] = [];

  if (data.releases.length > 0) {
    groups.push({
      label: "Releases",
      items: data.releases.map((r) => ({
        title: r.name,
        subtitle: r.albumArtist,
        href: `/release/${r.slug}`,
        imageUrl: r.imageUrl,
      })),
    });
  }

  if (data.artists.length > 0) {
    groups.push({
      label: "Artists",
      items: data.artists.map((a) => ({
        title: a.name,
        subtitle: a.role,
        href: `/artist/${a.slug}`,
        imageUrl: a.imageUrl,
      })),
    });
  }

  if (data.composers.length > 0) {
    groups.push({
      label: "Composers",
      items: data.composers.map((c) => ({
        title: c.name,
        subtitle: c.years,
        href: `/composer/${c.slug}`,
        imageUrl: c.imageUrl,
      })),
    });
  }

  if (data.works.length > 0) {
    groups.push({
      label: "Works",
      items: data.works.map((w) => ({
        title: w.title,
        subtitle: w.arrangerName ? `${w.composerName} (arr. ${w.arrangerName})` : w.composerName,
        href: `/work/${w.slug}`,
        imageUrl: w.imageUrl,
      })),
    });
  }

  if (data.blog.length > 0) {
    groups.push({
      label: "Blog",
      items: data.blog.map((b) => ({
        title: b.title,
        subtitle: b.category,
        href: `/blog/${b.slug}`,
        imageUrl: b.imageUrl,
      })),
    });
  }

  return groups;
}

/* ── Result card ── */

function SearchResultCard({ item, onClick }: { item: ResultItem; onClick: () => void }) {
  return (
    <button
      type="button"
      onClick={onClick}
      className="flex w-full items-center gap-3 px-4 py-2 text-left transition-colors duration-200 ease-in-out hover:bg-lightline/50 dark:hover:bg-darkline/50"
    >
      <div className="relative size-10 shrink-0 overflow-hidden rounded-lg">
        <Image
          src={item.imageUrl || ARTIST_PLACEHOLDER_SRC}
          alt=""
          fill
          sizes="40px"
          className="object-cover"
        />
      </div>
      <div className="min-w-0 flex-1">
        <p className="truncate text-sm text-lighttext dark:text-darktext">{item.title}</p>
        {item.subtitle && (
          <p className="truncate text-xs text-lightsec dark:text-darksec">{item.subtitle}</p>
        )}
      </div>
    </button>
  );
}

/* ── Main component ── */

export function GlobalSearch({ onOpenChange }: { onOpenChange?: (open: boolean) => void } = {}) {
  const [open, setOpen] = useState(false);
  const [expanded, setExpanded] = useState(false);
  const [query, setQuery] = useState("");
  const [results, setResults] = useState<SearchResults>(emptyResults);
  const [fetchedQuery, setFetchedQuery] = useState("");

  const debouncedQuery = useDebounced(query, 300);
  const router = useRouter();

  const containerRef = useRef<HTMLDivElement>(null);
  const inputRef = useRef<HTMLInputElement>(null);

  const close = useCallback(() => {
    setOpen(false);
    setQuery("");
    setResults(emptyResults);
  }, []);

  useClickOutside([containerRef], close, open);

  // Fetch results
  const abortRef = useRef<AbortController | null>(null);

  useEffect(() => {
    if (!debouncedQuery || debouncedQuery.length < 2) return;

    abortRef.current?.abort();
    const controller = new AbortController();
    abortRef.current = controller;

    fetch(`/api/search?q=${encodeURIComponent(debouncedQuery)}`, {
      signal: controller.signal,
    })
      .then((res) => res.json())
      .then((data: SearchResults) => {
        setResults(data);
        setFetchedQuery(debouncedQuery);
      })
      .catch(() => {
        if (!controller.signal.aborted) {
          setResults(emptyResults);
          setFetchedQuery(debouncedQuery);
        }
      });

    return () => controller.abort();
  }, [debouncedQuery]);

  // Derived state
  const activeResults = debouncedQuery && debouncedQuery.length >= 2 ? results : emptyResults;
  const loading = debouncedQuery.length >= 2 && fetchedQuery !== debouncedQuery;

  // Escape key
  useEffect(() => {
    if (!open) return;
    const onKeyDown = (e: KeyboardEvent) => {
      if (e.key === "Escape") close();
    };
    window.addEventListener("keydown", onKeyDown);
    return () => window.removeEventListener("keydown", onKeyDown);
  }, [open, close]);

  // Focus input when opening
  useEffect(() => {
    if (open) {
      requestAnimationFrame(() => inputRef.current?.focus());
    }
  }, [open]);

  const navigate = (href: string) => {
    close();
    router.push(href);
  };

  const groups = groupResults(activeResults);
  const hasResults = groups.length > 0;
  const showDropdown = open && debouncedQuery.length >= 2;

  return (
    <div
      ref={containerRef}
      className={`relative flex items-center gap-2 ${expanded ? "flex-1 md:flex-none" : ""}`}
    >
      {/* Search input */}
      <AnimatePresence
        onExitComplete={() => {
          setExpanded(false);
          onOpenChange?.(false);
        }}
      >
        {open && (
          <motion.div
            key="search-input"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            transition={fadeTransition}
            className="relative flex flex-1 items-center"
          >
            <IoSearchOutline className="absolute left-3.5 text-lightsec dark:text-darksec" />
            <input
              ref={inputRef}
              type="text"
              value={query}
              onChange={(e) => setQuery(e.target.value)}
              placeholder="Search..."
              className="relative z-10 w-full rounded-xl border border-lightline bg-lightbg px-4 py-3.5 text-sm text-lighttext shadow-lg transition-colors duration-200 outline-none placeholder:text-lightsec focus:border-lightline-hover md:min-w-80 dark:border-darkline dark:bg-darkbg dark:text-darktext dark:placeholder:text-darksec dark:focus:border-darkline-hover"
            />
          </motion.div>
        )}
      </AnimatePresence>

      {/* Button — always in place, icon swaps inside */}
      <IconButton
        onClick={() => {
          if (open) {
            close();
          } else {
            setOpen(true);
            setExpanded(true);
            onOpenChange?.(true);
          }
        }}
        aria-label={open ? "Close search" : "Search"}
      >
        <AnimatePresence mode="wait" initial={false}>
          {open ? (
            <motion.span
              key="close"
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              exit={{ opacity: 0 }}
              transition={{ duration: 0.15, ease: "easeInOut" }}
              className="flex"
            >
              <IoCloseOutline />
            </motion.span>
          ) : (
            <motion.span
              key="search"
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              exit={{ opacity: 0 }}
              transition={{ duration: 0.15, ease: "easeInOut" }}
              className="flex"
            >
              <IoSearchOutline />
            </motion.span>
          )}
        </AnimatePresence>
      </IconButton>

      {/* Results dropdown — matches full width of input + button */}
      <AnimatePresence>
        {showDropdown && (
          <motion.div
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            transition={fadeTransition}
            className="absolute top-full right-0 left-0 z-50 mt-2 overflow-hidden rounded-xl bg-lightbg shadow-xl ring ring-lightline transition-[box-shadow,color,background-color] duration-300 ease-in-out hover:ring-lightline-hover dark:border-darkline dark:bg-darkbg dark:ring-darkline dark:hover:ring-darkline-hover"
          >
            {loading && !hasResults && (
              <div className="p-4 text-sm text-lightsec dark:text-darksec">Searching...</div>
            )}

            {!loading && !hasResults && debouncedQuery.length >= 2 && (
              <div className="p-4 text-sm text-lightsec dark:text-darksec">No results found.</div>
            )}

            {hasResults && (
              <div className="max-h-120 overflow-y-auto pt-4 pb-2">
                {groups.map((group, gi) => (
                  <div key={group.label}>
                    {gi > 0 && (
                      <div className="mx-4 mt-4 border-t border-lightline pt-4 dark:border-darkline" />
                    )}
                    <p className="px-4 text-sm text-lightsec dark:text-darksec">{group.label}</p>
                    {group.items.map((item) => (
                      <SearchResultCard
                        key={item.href}
                        item={item}
                        onClick={() => navigate(item.href)}
                      />
                    ))}
                  </div>
                ))}
              </div>
            )}
          </motion.div>
        )}
      </AnimatePresence>
    </div>
  );
}
