"use client";

import React, { useState, useCallback, useEffect, useRef, useId } from "react";
import { useRouter } from "next/navigation";

// ─── Types ───────────────────────────────────────────────────────────────────

interface Suggestion {
  type: "plant" | "disease";
  id: string;
  label: string;
  subtitle: string;
  emoji: string;
  href: string;
}

export interface SearchSuggestionsProps {
  /** Placeholder text for the search input */
  placeholder?: string;
  /** Additional CSS classes for the search <input> element */
  inputClassName?: string;
  /** Additional CSS classes for the outer wrapper div */
  wrapperClassName?: string;
  /** Additional CSS classes for the <form> element */
  formClassName?: string;
  /** Called after a suggestion is clicked or the search is submitted (e.g., to close a mobile drawer) */
  onNavigate?: () => void;
}

// ─── Highlight helper ────────────────────────────────────────────────────────

/**
 * Splits `text` on case-insensitive occurrences of `query` and wraps each match
 * in a <mark> element so the user can see what part of the suggestion matched
 * their typed input.
 */
function highlightMatch(text: string, query: string): React.ReactNode {
  if (!query.trim()) return text;
  const escaped = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  const regex = new RegExp(`(${escaped})`, "gi");
  const parts = text.split(regex);
  const lowerQuery = query.toLowerCase();

  return parts.map((part, i) => {
    if (part.toLowerCase() === lowerQuery) {
      return (
        <mark
          key={i}
          className="bg-leaf-green-200 dark:bg-leaf-green-700 text-leaf-green-900 dark:text-leaf-green-100 rounded px-0.5"
        >
          {part}
        </mark>
      );
    }
    return part;
  });
}

// ─── Component ───────────────────────────────────────────────────────────────

/**
 * Search-as-you-type input with a debounced suggestions dropdown.
 *
 * - Fetches suggestions from `/api/plants/suggestions?q=...` as the user types
 * - Displays results in a dropdown with keyboard navigation (↑↓ Enter Escape)
 * - Clicking a suggestion navigates directly to the plant or disease page
 * - Pressing Enter (when no suggestion is highlighted) navigates to the browse
 *   page with the query as a search parameter
 */
export default function SearchSuggestions({
  placeholder = "Search plants...",
  inputClassName = "",
  wrapperClassName = "",
  formClassName = "",
  onNavigate,
}: SearchSuggestionsProps) {
  const router = useRouter();
  const inputId = useId();

  const [query, setQuery] = useState("");
  const [suggestions, setSuggestions] = useState<Suggestion[]>([]);
  const [showDropdown, setShowDropdown] = useState(false);
  const [activeIndex, setActiveIndex] = useState(-1);
  const [isLoading, setIsLoading] = useState(false);

  const inputRef = useRef<HTMLInputElement>(null);
  const dropdownRef = useRef<HTMLDivElement>(null);
  const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  // ─── Fetch suggestions with debounce ─────────────────────────────────────

  useEffect(() => {
    const trimmed = query.trim();

    // Empty query: don't fetch (the empty-input reset is handled in onChange).
    if (trimmed.length < 1) return;

    // Cancel any pending debounced fetch so we only fire the latest one.
    if (debounceRef.current) {
      clearTimeout(debounceRef.current);
    }

    // Track whether this particular effect cycle is still active, so stale
    // async responses don't overwrite later (or cleared) state.
    let cancelled = false;

    debounceRef.current = setTimeout(async () => {
      setIsLoading(true);
      try {
        const response = await fetch(`/api/plants/suggestions?q=${encodeURIComponent(trimmed)}`);
        if (!response.ok) {
          throw new Error(`HTTP ${response.status}`);
        }
        const data = await response.json();
        const items: Suggestion[] = data.suggestions ?? [];
        if (!cancelled) {
          setSuggestions(items);
          setShowDropdown(items.length > 0);
          setActiveIndex(-1);
        }
      } catch {
        if (!cancelled) {
          setSuggestions([]);
          setShowDropdown(false);
        }
      } finally {
        if (!cancelled) {
          setIsLoading(false);
        }
      }
    }, 200);

    return () => {
      cancelled = true;
      if (debounceRef.current) {
        clearTimeout(debounceRef.current);
      }
    };
  }, [query]);

  // ─── Close dropdown on outside click ─────────────────────────────────────

  useEffect(() => {
    const handleClickOutside = (e: MouseEvent) => {
      if (
        dropdownRef.current &&
        inputRef.current &&
        !dropdownRef.current.contains(e.target as Node) &&
        !inputRef.current.contains(e.target as Node)
      ) {
        setShowDropdown(false);
      }
    };

    document.addEventListener("mousedown", handleClickOutside);
    return () => document.removeEventListener("mousedown", handleClickOutside);
  }, []);

  // ─── Navigation helpers ──────────────────────────────────────────────────

  const navigate = useCallback(
    (href: string) => {
      setShowDropdown(false);
      setQuery("");
      setSuggestions([]);
      setActiveIndex(-1);
      router.push(href);
      onNavigate?.();
    },
    [router, onNavigate],
  );

  const submitQuery = useCallback(() => {
    const trimmed = query.trim();
    if (trimmed) {
      navigate(`/browse?search=${encodeURIComponent(trimmed)}`);
    } else {
      navigate("/browse");
    }
  }, [query, navigate]);

  // ─── Keyboard navigation ─────────────────────────────────────────────────

  const handleKeyDown = useCallback(
    (e: React.KeyboardEvent<HTMLInputElement>) => {
      if (!showDropdown || suggestions.length === 0) {
        if (e.key === "Enter") {
          e.preventDefault();
          submitQuery();
        }
        return;
      }

      switch (e.key) {
        case "ArrowDown":
          e.preventDefault();
          setActiveIndex((prev) => (prev < suggestions.length - 1 ? prev + 1 : 0));
          break;

        case "ArrowUp":
          e.preventDefault();
          setActiveIndex((prev) => (prev > 0 ? prev - 1 : suggestions.length - 1));
          break;

        case "Enter":
          e.preventDefault();
          if (activeIndex >= 0 && activeIndex < suggestions.length) {
            navigate(suggestions[activeIndex].href);
          } else {
            submitQuery();
          }
          break;

        case "Escape":
          e.preventDefault();
          setShowDropdown(false);
          setActiveIndex(-1);
          inputRef.current?.blur();
          break;
      }
    },
    [showDropdown, suggestions, activeIndex, submitQuery, navigate],
  );

  // ─── Suggestion click (uses mousedown so it fires before blur) ───────────

  const handleSuggestionClick = useCallback(
    (href: string) => (e: React.MouseEvent) => {
      e.preventDefault();
      navigate(href);
    },
    [navigate],
  );

  // ─── Input change handler: syncs query state AND resets suggestions
  //      when the user clears the input (avoids doing setState in the effect).
  // ───────────────────────────────────────────────────────────────────────────

  const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value;
    setQuery(value);

    // When the input is cleared, immediately reset the suggestion state
    // instead of doing it inside the effect (which would trigger a
    // cascading-render warning).
    if (!value.trim()) {
      setSuggestions([]);
      setShowDropdown(false);
      setActiveIndex(-1);
      setIsLoading(false);
    }
  }, []);

  // ─── Render ──────────────────────────────────────────────────────────────

  return (
    <div className={`relative ${wrapperClassName}`}>
      <form
        className={formClassName}
        onSubmit={(e) => {
          e.preventDefault();
          submitQuery();
        }}
        role="search"
      >
        <div className="relative">
          <label htmlFor={inputId} className="sr-only">
            {placeholder}
          </label>

          <input
            ref={inputRef}
            id={inputId}
            type="search"
            placeholder={placeholder}
            value={query}
            onChange={handleInputChange}
            onFocus={() => {
              if (suggestions.length > 0) setShowDropdown(true);
            }}
            onKeyDown={handleKeyDown}
            className={inputClassName}
            autoComplete="off"
            aria-expanded={showDropdown}
            aria-haspopup="listbox"
            aria-autocomplete="list"
            aria-controls={showDropdown ? `${inputId}-listbox` : undefined}
            aria-activedescendant={
              activeIndex >= 0 ? `${inputId}-option-${activeIndex}` : undefined
            }
          />

          {/* Loading spinner */}
          {isLoading && (
            <div className="absolute right-3 top-1/2 -translate-y-1/2" aria-hidden="true">
              <div className="h-4 w-4 animate-spin rounded-full border-2 border-zinc-300 border-t-leaf-green-600 dark:border-zinc-600 dark:border-t-leaf-green-400" />
            </div>
          )}
        </div>
      </form>

      {/* Suggestions dropdown */}
      {showDropdown && suggestions.length > 0 && (
        <div
          ref={dropdownRef}
          id={`${inputId}-listbox`}
          role="listbox"
          aria-label="Search suggestions"
          className="absolute z-50 mt-1 w-full rounded-lg border border-zinc-200 dark:border-zinc-700 bg-white dark:bg-zinc-900 shadow-lg overflow-hidden"
        >
          {suggestions.map((suggestion, index) => (
            <button
              key={`${suggestion.type}-${suggestion.id}`}
              id={`${inputId}-option-${index}`}
              type="button"
              role="option"
              aria-selected={index === activeIndex}
              onMouseDown={handleSuggestionClick(suggestion.href)}
              className={`w-full flex items-center gap-3 px-3.5 py-2.5 text-left transition-colors ${
                index === activeIndex
                  ? "bg-leaf-green-50 dark:bg-leaf-green-900/30"
                  : "hover:bg-zinc-50 dark:hover:bg-zinc-800"
              }`}
            >
              {/* Emoji */}
              <span className="text-xl shrink-0" aria-hidden="true">
                {suggestion.emoji}
              </span>

              {/* Text */}
              <div className="min-w-0 flex-1">
                <div className="text-sm font-medium text-zinc-900 dark:text-zinc-100 truncate">
                  {highlightMatch(suggestion.label, query)}
                </div>
                <div className="text-xs text-zinc-500 dark:text-zinc-400 truncate mt-0.5">
                  {highlightMatch(suggestion.subtitle, query)}
                </div>
              </div>

              {/* Type badge */}
              <span className="text-[10px] uppercase tracking-wider text-zinc-400 dark:text-zinc-500 shrink-0 ml-1">
                {suggestion.type === "plant" ? "Plant" : "Disease"}
              </span>
            </button>
          ))}
        </div>
      )}
    </div>
  );
}
