"use client";

import { createPortal } from "react-dom";
import { useCallback, useEffect, useRef, useState } from "react";
import { useAnchoredOverlay } from "~/hooks/useAnchoredOverlay.js";

export interface AutocompleteOption {
  id: string;
  label: string;
  sub?: string; // secondary label shown in smaller text
}

interface AutocompleteInputProps {
  options: AutocompleteOption[];
  value: string; // selected id
  onChange: (id: string) => void;
  placeholder?: string;
  className?: string;
}

function normalize(s: string) {
  return s.toLowerCase().replace(/[-_\s]/g, "");
}

function scoreMatch(option: AutocompleteOption, query: string): number {
  const q = normalize(query);
  const label = normalize(option.label);
  const sub = normalize(option.sub ?? "");
  if (label.startsWith(q) || sub.startsWith(q)) return 2;
  if (label.includes(q) || sub.includes(q)) return 1;
  return 0;
}

export function AutocompleteInput({ options, value, onChange, placeholder = "Search…", className }: AutocompleteInputProps) {
  const selected = options.find((o) => o.id === value);
  const [input, setInput] = useState(selected?.label ?? "");
  const [open, setOpen] = useState(false);
  const [activeIdx, setActiveIdx] = useState(0);
  const containerRef = useRef<HTMLDivElement>(null);
  const inputRef = useRef<HTMLInputElement>(null);
  const closeDropdown = useCallback(() => {
    setOpen(false);
    setInput(selected?.label ?? "");
  }, [selected?.label]);
  const { panelRef, position } = useAnchoredOverlay<HTMLDivElement>({
    open,
    onClose: closeDropdown,
    align: "start",
    matchTriggerWidth: true,
    triggerRef: containerRef,
  });

  // Keep input text in sync when selected value changes externally
  useEffect(() => {
    setInput(selected?.label ?? "");
  }, [selected?.label]);

  const filtered = input.trim() === "" || (selected && input === selected.label)
    ? options.slice(0, 20)
    : options
        .map((o) => ({ o, score: scoreMatch(o, input) }))
        .filter(({ score }) => score > 0)
        .sort((a, b) => b.score - a.score)
        .map(({ o }) => o)
        .slice(0, 20);

  function select(opt: AutocompleteOption) {
    onChange(opt.id);
    setInput(opt.label);
    setOpen(false);
    setActiveIdx(0);
  }

  function clear() {
    onChange("");
    setInput("");
    setOpen(true);
    setActiveIdx(0);
    inputRef.current?.focus();
  }

  function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
    if (e.key === "ArrowDown") { e.preventDefault(); setActiveIdx((i) => Math.min(i + 1, filtered.length - 1)); }
    else if (e.key === "ArrowUp") { e.preventDefault(); setActiveIdx((i) => Math.max(i - 1, 0)); }
    else if (e.key === "Enter") {
      e.preventDefault();
      const opt = filtered[activeIdx];
      if (opt) select(opt);
    } else if (e.key === "Escape") {
      setOpen(false);
      // Restore display text to match current selection
      setInput(selected?.label ?? "");
    }
  }

  return (
    <div ref={containerRef} className={`relative ${className ?? ""}`}>
      <div className="relative">
        <input
          ref={inputRef}
          type="text"
          value={input}
          onChange={(e) => {
            setInput(e.target.value);
            setOpen(true);
            setActiveIdx(0);
            // Clear selection if user edits away from selected label
            if (selected && e.target.value !== selected.label) onChange("");
          }}
          onFocus={() => setOpen(true)}
          onKeyDown={handleKeyDown}
          placeholder={placeholder}
          className="w-full px-3 py-2 pr-7 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500 text-sm bg-white dark:bg-gray-900 dark:text-gray-100 dark:placeholder-gray-500"
        />
        {value && (
          <button
            type="button"
            onClick={clear}
            className="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 leading-none text-base"
            aria-label="Clear"
          >
            ×
          </button>
        )}
      </div>

      {open && filtered.length > 0 && typeof document !== "undefined"
        ? createPortal(
            <div
              ref={panelRef}
              className="fixed z-[9998] max-h-56 overflow-y-auto rounded-lg border border-gray-200 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-800"
              style={{
                top: position.top,
                left: position.left,
                width: position.minWidth,
              }}
              onMouseDown={(e) => e.preventDefault()}
            >
              <ul>
                {filtered.map((opt, idx) => (
                  <li key={opt.id}>
                    <button
                      type="button"
                      onMouseDown={(e) => { e.preventDefault(); select(opt); }}
                      className={`flex w-full items-baseline gap-2 px-3 py-2 text-left text-sm transition-colors ${
                        idx === activeIdx
                          ? "bg-brand-50 text-brand-700 dark:bg-brand-900/30 dark:text-brand-300"
                          : "text-gray-700 hover:bg-gray-50 dark:text-gray-200 dark:hover:bg-gray-700"
                      }`}
                    >
                      <span className="truncate">{opt.label}</span>
                      {opt.sub && <span className="shrink-0 text-xs text-gray-400 dark:text-gray-500">{opt.sub}</span>}
                    </button>
                  </li>
                ))}
              </ul>
            </div>,
            document.body,
          )
        : null}
    </div>
  );
}
