import { useEffect, useMemo, useRef, useState } from 'react';
import type { CSSProperties, RefObject } from 'react';
import { createPortal } from 'react-dom';
import { cn } from '@/lib/utils';

export interface SlashCommandItem {
  name: string;
  description?: string;
}

interface Props {
  query: string;
  items: SlashCommandItem[];
  inputRef: RefObject<HTMLElement | null>;
  onSelect: (name: string) => void;
  onClose: () => void;
  emptyLabel?: string;
}

const DROPDOWN_HEIGHT_BUDGET = 320;

function filterByPrefix(items: SlashCommandItem[], query: string): SlashCommandItem[] {
  const q = query.toLowerCase();
  const filtered = q ? items.filter((s) => s.name.toLowerCase().startsWith(q)) : items;
  return [...filtered].sort((a, b) => a.name.localeCompare(b.name));
}

export function SlashCommandPicker({
  query,
  items,
  inputRef,
  onSelect,
  onClose,
  emptyLabel = 'No commands available',
}: Props) {
  const [highlightIndex, setHighlightIndex] = useState(0);
  const popoverRef = useRef<HTMLDivElement>(null);
  const filtered = useMemo(() => filterByPrefix(items, query), [items, query]);

  const [rect, setRect] = useState<DOMRect | null>(
    () => inputRef.current?.getBoundingClientRect() ?? null,
  );
  const [vvTick, setVvTick] = useState(0);

  useEffect(() => { setHighlightIndex(0); }, [query]);

  useEffect(() => {
    function recalc() {
      setRect(inputRef.current?.getBoundingClientRect() ?? null);
      setVvTick((t) => t + 1);
    }
    recalc();
    const vv = window.visualViewport;
    vv?.addEventListener('resize', recalc);
    vv?.addEventListener('scroll', recalc);
    window.addEventListener('resize', recalc);
    return () => {
      vv?.removeEventListener('resize', recalc);
      vv?.removeEventListener('scroll', recalc);
      window.removeEventListener('resize', recalc);
    };
  }, [inputRef]);

  useEffect(() => {
    function handleKeyDown(e: KeyboardEvent) {
      if (e.key === 'ArrowDown') {
        e.preventDefault();
        setHighlightIndex((prev) => (prev < filtered.length - 1 ? prev + 1 : 0));
      } else if (e.key === 'ArrowUp') {
        e.preventDefault();
        setHighlightIndex((prev) => (prev > 0 ? prev - 1 : filtered.length - 1));
      } else if (e.key === 'Enter' || e.key === 'Tab') {
        if (filtered.length === 0) return;
        e.preventDefault();
        const target = filtered[highlightIndex] ?? filtered[0];
        if (target) onSelect(target.name);
      } else if (e.key === 'Escape') {
        e.preventDefault();
        onClose();
      }
    }
    document.addEventListener('keydown', handleKeyDown);
    return () => document.removeEventListener('keydown', handleKeyDown);
  }, [filtered, highlightIndex, onSelect, onClose]);

  useEffect(() => {
    function handleMouseDown(e: MouseEvent) {
      if (popoverRef.current && !popoverRef.current.contains(e.target as Node)) {
        onClose();
      }
    }
    document.addEventListener('mousedown', handleMouseDown);
    return () => document.removeEventListener('mousedown', handleMouseDown);
  }, [onClose]);

  useEffect(() => {
    const el = popoverRef.current?.querySelector('[data-highlighted="true"]');
    if (el) el.scrollIntoView({ block: 'nearest' });
  }, [highlightIndex]);

  const style = useMemo<CSSProperties>(() => {
    if (!rect) return { display: 'none' };
    const vv = window.visualViewport;
    const vvOffsetTop = vv?.offsetTop ?? 0;
    const vvHeight = vv?.height ?? window.innerHeight;
    // Visible region in layout-viewport coords (what position:fixed uses)
    const visibleTop = vvOffsetTop;
    const visibleBottom = vvOffsetTop + vvHeight;

    const spaceAbove = rect.top - visibleTop;
    const spaceBelow = visibleBottom - rect.bottom;

    if (spaceAbove >= Math.min(DROPDOWN_HEIGHT_BUDGET, spaceBelow)) {
      // Place above: clamp to visible top
      const popupTop = Math.max(visibleTop, rect.top - DROPDOWN_HEIGHT_BUDGET);
      return {
        position: 'fixed',
        top: popupTop,
        left: rect.left,
        maxHeight: rect.top - popupTop,
      };
    }
    // Place below: clamp to visible bottom
    return {
      position: 'fixed',
      top: rect.bottom,
      left: rect.left,
      maxHeight: Math.min(DROPDOWN_HEIGHT_BUDGET, visibleBottom - rect.bottom),
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [rect, vvTick]);

  const popover = filtered.length === 0 ? (
    <div
      ref={popoverRef}
      className="z-50 bg-popover border border-border rounded-md shadow min-w-[320px] p-2"
      style={style}
    >
      <div className="text-xs text-muted-foreground px-2 py-1">
        {query ? `No command starts with "/${query}"` : emptyLabel}
      </div>
    </div>
  ) : (
    <div
      ref={popoverRef}
      className="z-50 bg-popover border border-border rounded-md shadow min-w-[320px] max-w-[420px] max-h-[320px] overflow-y-auto overscroll-contain touch-pan-y"
      style={style}
    >
      {filtered.map((item, i) => (
        <div
          key={item.name}
          role="option"
          aria-selected={i === highlightIndex}
          data-highlighted={i === highlightIndex}
          className={cn(
            'w-full text-left px-2.5 py-2 cursor-pointer block',
            i === highlightIndex && 'bg-muted',
          )}
          onMouseEnter={() => setHighlightIndex(i)}
          onClick={() => onSelect(item.name)}
        >
          <div className="font-mono text-xs font-bold text-foreground">/{item.name}</div>
          {item.description && (
            <div
              className="text-xs text-muted-foreground overflow-hidden"
              style={{
                display: '-webkit-box',
                WebkitLineClamp: 2,
                WebkitBoxOrient: 'vertical',
              }}
            >
              {item.description}
            </div>
          )}
        </div>
      ))}
    </div>
  );

  return createPortal(popover, document.body);
}
