import {
  ButtonHTMLAttributes,
  HTMLAttributes,
  ReactNode,
  createContext,
  useCallback,
  useContext,
  useEffect,
  useRef,
  useState,
} from 'react';
import { cn } from '../lib/cn';

interface DropdownCtx {
  open: boolean;
  setOpen: (v: boolean) => void;
  rootRef: React.RefObject<HTMLDivElement>;
}

const Ctx = createContext<DropdownCtx | null>(null);

function useDropdown() {
  const ctx = useContext(Ctx);
  if (!ctx) throw new Error('Dropdown.* must be used inside <Dropdown>');
  return ctx;
}

export interface DropdownProps extends HTMLAttributes<HTMLDivElement> {
  defaultOpen?: boolean;
  open?: boolean;
  onOpenChange?: (v: boolean) => void;
  children?: ReactNode;
}

export function Dropdown({
  defaultOpen = false,
  open: controlledOpen,
  onOpenChange,
  className,
  children,
  ...rest
}: DropdownProps) {
  const [uncontrolled, setUncontrolled] = useState(defaultOpen);
  const isControlled = controlledOpen !== undefined;
  const open = isControlled ? !!controlledOpen : uncontrolled;
  const rootRef = useRef<HTMLDivElement>(null);

  const setOpen = useCallback(
    (v: boolean) => {
      if (!isControlled) setUncontrolled(v);
      onOpenChange?.(v);
    },
    [isControlled, onOpenChange],
  );

  useEffect(() => {
    if (!open) return;
    const onDown = (e: MouseEvent) => {
      if (rootRef.current && !rootRef.current.contains(e.target as Node)) {
        setOpen(false);
      }
    };
    const onKey = (e: KeyboardEvent) => {
      if (e.key === 'Escape') setOpen(false);
    };
    document.addEventListener('mousedown', onDown);
    document.addEventListener('keydown', onKey);
    return () => {
      document.removeEventListener('mousedown', onDown);
      document.removeEventListener('keydown', onKey);
    };
  }, [open, setOpen]);

  return (
    <Ctx.Provider value={{ open, setOpen, rootRef }}>
      <div ref={rootRef} className={cn('relative inline-block', className)} {...rest}>
        {children}
      </div>
    </Ctx.Provider>
  );
}

interface TriggerProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  children?: ReactNode;
}

Dropdown.Trigger = function DropdownTrigger({
  className,
  children,
  onClick,
  ...rest
}: TriggerProps) {
  const { open, setOpen } = useDropdown();
  return (
    <button
      type="button"
      aria-haspopup="menu"
      aria-expanded={open}
      onClick={(e) => {
        setOpen(!open);
        onClick?.(e);
      }}
      className={cn(
        'inline-flex items-center gap-2 px-3 h-9 rounded-lg text-sm bg-surface border border-border text-text hover:bg-surface-2 transition-colors',
        className,
      )}
      {...rest}
    >
      {children}
    </button>
  );
};

interface MenuProps extends HTMLAttributes<HTMLDivElement> {
  align?: 'start' | 'end';
  children?: ReactNode;
}

Dropdown.Menu = function DropdownMenu({
  align = 'start',
  className,
  children,
  ...rest
}: MenuProps) {
  const { open } = useDropdown();
  if (!open) return null;
  return (
    <div
      role="menu"
      className={cn(
        'absolute z-50 mt-2 min-w-[180px] py-1 rounded-lg bg-surface border border-border shadow-lg',
        align === 'end' ? 'right-0' : 'left-0',
        className,
      )}
      {...rest}
    >
      {children}
    </div>
  );
};

interface ItemProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: 'default' | 'danger';
  children?: ReactNode;
}

Dropdown.Item = function DropdownItem({
  variant = 'default',
  className,
  children,
  onClick,
  ...rest
}: ItemProps) {
  const { setOpen } = useDropdown();
  return (
    <button
      type="button"
      role="menuitem"
      onClick={(e) => {
        onClick?.(e);
        setOpen(false);
      }}
      className={cn(
        'w-full text-left px-3 py-2 text-sm text-text hover:bg-surface-2 transition-colors',
        variant === 'danger' && 'text-danger hover:bg-danger-soft',
        className,
      )}
      {...rest}
    >
      {children}
    </button>
  );
};

Dropdown.Separator = function DropdownSeparator({
  className,
  ...rest
}: HTMLAttributes<HTMLDivElement>) {
  return <div className={cn('my-1 h-px bg-rule', className)} role="separator" {...rest} />;
};
