'use client';

import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';
import { useRouter } from 'next/navigation';
import { ArrowRightIcon, BuildingIcon, ClockIcon, MapPinIcon, ScaleIcon, SearchIcon, Spinner, TrendingUpIcon, UserIcon, XIcon } from './Icons';

type SuggestionType = 'ranking' | 'lawyer' | 'firm' | 'city' | 'practice_area';
type Suggestion = { type: SuggestionType; label: string; sublabel?: string; href: string };
/** A row in the dropdown: a suggestion, a recent/popular query, or the "search for …" action */
type Row = { key: string; kind: 'suggestion' | 'recent' | 'popular' | 'query'; label: string; sublabel?: string; href: string; type?: SuggestionType };

const TYPE_LABEL: Record<SuggestionType, string> = { ranking: 'Ranking', lawyer: 'Lawyer', firm: 'Firm', city: 'City', practice_area: 'Practice area' };
const TYPE_ICON: Record<SuggestionType, typeof UserIcon> = { ranking: TrendingUpIcon, lawyer: UserIcon, firm: BuildingIcon, city: MapPinIcon, practice_area: ScaleIcon };

const POPULAR = ['Divorce lawyers in New York', 'Personal injury lawyers in Los Angeles', 'Immigration lawyers in Houston', 'Criminal defense in Chicago'];
const RECENT_KEY = 'lr_recent_searches';
const searchHref = (q: string) => `/search/?q=${encodeURIComponent(q)}`;

function readRecent(): string[] {
  try {
    const v = JSON.parse(localStorage.getItem(RECENT_KEY) ?? '[]');
    return Array.isArray(v) ? v.filter((x) => typeof x === 'string').slice(0, 5) : [];
  } catch {
    return [];
  }
}
function saveRecent(q: string) {
  try {
    const next = [q, ...readRecent().filter((x) => x.toLowerCase() !== q.toLowerCase())].slice(0, 5);
    localStorage.setItem(RECENT_KEY, JSON.stringify(next));
  } catch {
    /* storage blocked — recents are a convenience only */
  }
}

/** Bold the part of a label that matches what was typed */
function Highlight({ text, query }: { text: string; query: string }) {
  const words = query.trim().toLowerCase().split(/\s+/).filter((w) => w.length >= 2);
  if (!words.length) return <>{text}</>;
  const re = new RegExp(`(${words.map((w) => w.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|')})`, 'i');
  // split() with a capturing group puts the matched pieces at the odd indexes
  return (
    <>
      {text.split(re).map((part, i) =>
        i % 2 === 1 ? (
          <mark key={i} className="bg-transparent font-semibold text-ink">
            {part}
          </mark>
        ) : (
          <span key={i}>{part}</span>
        ),
      )}
    </>
  );
}

export function SearchBox({ compact = false, defaultValue = '', autoFocus = false }: { compact?: boolean; defaultValue?: string; autoFocus?: boolean }) {
  const router = useRouter();
  const [q, setQ] = useState(defaultValue);
  const [items, setItems] = useState<Suggestion[]>([]);
  const [recent, setRecent] = useState<string[]>([]);
  const [open, setOpen] = useState(false);
  const [loading, setLoading] = useState(false);
  const [failed, setFailed] = useState(false);
  const [active, setActive] = useState(-1);
  const [touched, setTouched] = useState(false);
  const id = useId();
  const listId = `${id}-list`;
  const root = useRef<HTMLFormElement>(null);
  const input = useRef<HTMLInputElement>(null);

  // Keep the box in step with the URL when the same component survives a navigation (/search/?q=…)
  useEffect(() => {
    setQ(defaultValue);
    setTouched(false);
  }, [defaultValue]);

  // Debounced suggestions — only once the user has typed, so a pre-filled box never pops open on load
  useEffect(() => {
    const term = q.trim();
    if (!touched || term.length < 2) {
      setItems([]);
      setLoading(false);
      setFailed(false);
      return;
    }
    const ctrl = new AbortController();
    setLoading(true);
    const timer = setTimeout(async () => {
      try {
        const res = await fetch(`/api/v1/search/suggest/?q=${encodeURIComponent(term)}`, { signal: ctrl.signal });
        if (!res.ok) throw new Error(String(res.status));
        setItems(await res.json());
        setFailed(false);
      } catch (e) {
        if ((e as Error).name === 'AbortError') return;
        setItems([]);
        setFailed(true);
      }
      setLoading(false);
      setActive(-1);
    }, 160);
    return () => {
      clearTimeout(timer);
      ctrl.abort();
    };
  }, [q, touched]);

  // Close when clicking anywhere outside the box
  useEffect(() => {
    if (!open) return;
    const onDown = (e: MouseEvent) => {
      if (!root.current?.contains(e.target as Node)) setOpen(false);
    };
    document.addEventListener('mousedown', onDown);
    return () => document.removeEventListener('mousedown', onDown);
  }, [open]);

  // "/" focuses the header search from anywhere (unless the user is already typing somewhere)
  useEffect(() => {
    if (!compact) return;
    const onKey = (e: KeyboardEvent) => {
      const t = e.target as HTMLElement;
      if (e.key !== '/' || e.metaKey || e.ctrlKey || e.altKey || t.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(t.tagName)) return;
      e.preventDefault();
      input.current?.focus();
    };
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [compact]);

  const term = q.trim();
  const rows: Row[] = useMemo(() => {
    if (term.length < 2) {
      const r = recent.map((x) => ({ key: `r:${x}`, kind: 'recent' as const, label: x, href: searchHref(x) }));
      const p = POPULAR.filter((x) => !recent.some((y) => y.toLowerCase() === x.toLowerCase()))
        .slice(0, Math.max(0, 6 - r.length))
        .map((x) => ({ key: `p:${x}`, kind: 'popular' as const, label: x, href: searchHref(x) }));
      return [...r, ...p];
    }
    return [
      ...items.map((s) => ({ key: `s:${s.href}`, kind: 'suggestion' as const, label: s.label, sublabel: s.sublabel, href: s.href, type: s.type })),
      { key: 'q', kind: 'query' as const, label: term, href: searchHref(term) },
    ];
  }, [term, items, recent]);

  const go = useCallback(
    (row: Row | null) => {
      const text = row?.kind === 'recent' || row?.kind === 'popular' ? row.label : term;
      if (row?.kind !== 'suggestion' && text) saveRecent(text);
      setOpen(false);
      setActive(-1);
      input.current?.blur();
      router.push(row ? row.href : text ? searchHref(text) : '/search/');
    },
    [router, term],
  );

  function onSubmit(e: React.FormEvent) {
    e.preventDefault();
    go(open && active >= 0 ? (rows[active] ?? null) : null);
  }

  function onKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
    if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
      e.preventDefault();
      if (!open) {
        setOpen(true);
        return;
      }
      if (!rows.length) return;
      const last = rows.length - 1;
      setActive((a) => (e.key === 'ArrowDown' ? (a >= last ? 0 : a + 1) : a <= 0 ? last : a - 1));
    } else if (e.key === 'Escape') {
      if (open) {
        e.preventDefault();
        setOpen(false);
        setActive(-1);
      } else if (q) setQ('');
    } else if (e.key === 'Tab') setOpen(false);
  }

  const showPanel = open && (rows.length > 0 || loading || failed);
  const activeId = open && active >= 0 && rows[active] ? `${id}-opt-${active}` : undefined;
  const firstQueryGroup = rows.findIndex((r) => r.kind === 'popular');

  return (
    <form ref={root} role="search" onSubmit={onSubmit} className="relative">
      <label htmlFor={`${id}-input`} className="sr-only">
        Search lawyers, firms, cities or practice areas
      </label>
      <div
        className={`group flex items-center rounded-xl border bg-surface transition-[border-color,box-shadow] duration-150 focus-within:border-focus focus-within:shadow-[0_0_0_3px_color-mix(in_srgb,var(--focus)_20%,transparent)] ${
          compact ? 'border-rule-strong hover:border-muted' : 'border-rule-strong shadow-md hover:border-muted'
        }`}
      >
        <SearchIcon className={`shrink-0 text-muted transition-colors group-focus-within:text-ledger ${compact ? 'ml-3 h-4 w-4' : 'ml-4 h-5 w-5'}`} />
        <input
          ref={input}
          id={`${id}-input`}
          value={q}
          autoFocus={autoFocus}
          onChange={(e) => {
            setQ(e.target.value);
            setTouched(true);
            setOpen(true);
            setActive(-1);
          }}
          onFocus={() => {
            setRecent(readRecent());
            setOpen(true);
          }}
          onKeyDown={onKeyDown}
          role="combobox"
          aria-expanded={showPanel}
          aria-controls={listId}
          aria-autocomplete="list"
          aria-activedescendant={activeId}
          autoComplete="off"
          spellCheck={false}
          enterKeyHint="search"
          maxLength={120}
          placeholder={compact ? 'Search lawyers, firms, cities…' : 'Try “divorce lawyer in Chicago”'}
          className={`min-w-0 flex-1 bg-transparent text-ink placeholder:text-muted focus:outline-none ${compact ? 'h-10 px-2.5 text-sm' : 'h-14 px-3 text-base sm:text-lg'}`}
        />
        {loading && <Spinner className={`mr-1 text-muted ${compact ? 'h-4 w-4' : 'h-5 w-5'}`} />}
        {q && !loading && (
          <button
            type="button"
            aria-label="Clear search"
            onClick={() => {
              setQ('');
              setItems([]);
              setActive(-1);
              input.current?.focus();
            }}
            className="mr-1 grid h-8 w-8 place-items-center rounded-full text-muted transition-colors hover:bg-sunken hover:text-ink active:bg-rule"
          >
            <XIcon className="h-4 w-4" />
          </button>
        )}
        {compact && !q && (
          <kbd className="kbd mr-2.5 hidden lg:inline-flex" aria-hidden>
            /
          </kbd>
        )}
        {!compact && (
          <button type="submit" className="btn btn-primary btn-lg m-1.5 !min-h-11 rounded-lg px-4 sm:px-6">
            <span className="hidden sm:inline">Search</span>
            <ArrowRightIcon className="h-5 w-5 sm:hidden" />
            <span className="sr-only sm:hidden">Search</span>
          </button>
        )}
      </div>

      {showPanel && (
        <div className="pop-in absolute inset-x-0 z-40 mt-2 overflow-hidden rounded-xl border border-rule bg-surface shadow-lg">
          {term.length < 2 && rows.length > 0 && (
            <p className="px-4 pb-1 pt-3 text-[11px] font-bold uppercase tracking-[0.08em] text-muted">{recent.length ? 'Recent searches' : 'Popular searches'}</p>
          )}
          {failed && !items.length && <p className="px-4 pt-3 text-sm text-muted">Suggestions are unavailable right now — press Enter to search.</p>}
          <ul id={listId} role="listbox" aria-label="Search suggestions" className="max-h-[min(26rem,60vh)] overflow-y-auto py-1.5">
            {rows.map((r, i) => {
              const Icon = r.kind === 'suggestion' && r.type ? TYPE_ICON[r.type] : r.kind === 'recent' ? ClockIcon : r.kind === 'popular' ? TrendingUpIcon : SearchIcon;
              const isActive = i === active;
              return (
                <li key={r.key} role="presentation">
                  {i === firstQueryGroup && recent.length > 0 && <p className="px-4 pb-1 pt-3 text-[11px] font-bold uppercase tracking-[0.08em] text-muted">Popular searches</p>}
                  {r.kind === 'query' && items.length > 0 && <div className="mx-3 my-1.5 border-t border-rule" />}
                  <div
                    id={`${id}-opt-${i}`}
                    role="option"
                    aria-selected={isActive}
                    onMouseEnter={() => setActive(i)}
                    onMouseDown={(e) => e.preventDefault()}
                    onClick={() => go(r)}
                    className={`mx-1.5 flex items-center gap-3 rounded-lg px-2.5 py-2 text-sm transition-colors ${isActive ? 'bg-ledger-tint text-ink' : 'text-ink-2'}`}
                  >
                    <span className={`grid h-8 w-8 shrink-0 place-items-center rounded-lg ${r.type === 'ranking' ? 'bg-brass-soft text-brass' : isActive ? 'bg-ledger-soft text-ledger' : 'bg-sunken text-muted'}`}>
                      <Icon className="h-4 w-4" />
                    </span>
                    <span className="min-w-0 flex-1">
                      {r.kind === 'query' ? (
                        <span className="block truncate">
                          Search all lawyers for <strong className="font-semibold text-ink">“{r.label}”</strong>
                        </span>
                      ) : (
                        <>
                          <span className="block truncate text-ink">{r.kind === 'suggestion' ? <Highlight text={r.label} query={term} /> : r.label}</span>
                          {r.sublabel && <span className="block truncate text-xs text-muted">{r.sublabel}</span>}
                        </>
                      )}
                    </span>
                    {r.type && <span className="hidden shrink-0 rounded-md border border-rule px-1.5 py-0.5 text-[11px] font-medium text-muted sm:inline">{TYPE_LABEL[r.type]}</span>}
                    {isActive && <ArrowRightIcon className="h-4 w-4 shrink-0 text-ledger" />}
                  </div>
                </li>
              );
            })}
          </ul>
          {term.length >= 2 && !loading && !failed && items.length === 0 && (
            <p className="border-t border-rule px-4 py-2.5 text-xs text-muted">No direct matches — the full search also checks biographies and firms.</p>
          )}
          <p className="hidden items-center gap-3 border-t border-rule bg-paper px-4 py-2 text-[11px] text-muted sm:flex">
            <span className="inline-flex items-center gap-1">
              <kbd className="kbd">↑</kbd>
              <kbd className="kbd">↓</kbd> to move
            </span>
            <span className="inline-flex items-center gap-1">
              <kbd className="kbd">Enter</kbd> to open
            </span>
            <span className="inline-flex items-center gap-1">
              <kbd className="kbd">Esc</kbd> to close
            </span>
          </p>
        </div>
      )}
    </form>
  );
}
