import { Check, CaretDown } from "@phosphor-icons/react";
import { useEffect, useId, useRef, useState, type CSSProperties, type KeyboardEvent, type RefObject } from "react";
import { createPortal } from "react-dom";

export type BottomControlSelectOption<TValue extends string> = {
  value: TValue;
  label: string;
};

export type BottomControlSelectMenuProps<TValue extends string> = {
  value: TValue;
  options: readonly BottomControlSelectOption<TValue>[];
  "aria-label": string;
  label?: string;
  placement?: "top" | "bottom" | "inline";
  onValueChange: (value: TValue) => void;
};

export function BottomControlSelectMenu<TValue extends string>({ value, options, label, placement = "top", onValueChange, ...props }: BottomControlSelectMenuProps<TValue>) {
  const rootRef = useRef<HTMLDivElement>(null);
  const buttonRef = useRef<HTMLButtonElement>(null);
  const menuRef = useRef<HTMLDivElement>(null);
  const listboxId = useId();
  const [open, setOpen] = useState(false);
  const [menuStyle, setMenuStyle] = useState<CSSProperties>();
  const selectedOption = options.find((option) => option.value === value) ?? options[0];

  useEffect(() => {
    if (!open) return;

    const updateMenuPosition = () => {
      if (placement === "inline") return;
      const rect = buttonRef.current?.getBoundingClientRect();
      if (!rect) return;
      const gap = 8;
      const maxHeight = Math.max(160, placement === "bottom" ? window.innerHeight - rect.bottom - gap * 2 : rect.top - gap * 2);
      setMenuStyle({
        position: "fixed",
        left: rect.left,
        top: placement === "bottom" ? rect.bottom + gap : undefined,
        bottom: placement === "top" ? window.innerHeight - rect.top + gap : undefined,
        width: Math.max(rect.width, 192),
        maxHeight,
      });
    };

    updateMenuPosition();

    const handlePointerDown = (event: PointerEvent) => {
      const target = event.target as Node;
      if (!rootRef.current?.contains(target) && !menuRef.current?.contains(target)) setOpen(false);
    };

    const handleKeyDown = (event: globalThis.KeyboardEvent) => {
      if (event.key === "Escape") {
        setOpen(false);
        buttonRef.current?.focus();
      }
    };

    window.addEventListener("pointerdown", handlePointerDown);
    window.addEventListener("keydown", handleKeyDown);
    window.addEventListener("resize", updateMenuPosition);
    window.addEventListener("scroll", updateMenuPosition, true);
    return () => {
      window.removeEventListener("pointerdown", handlePointerDown);
      window.removeEventListener("keydown", handleKeyDown);
      window.removeEventListener("resize", updateMenuPosition);
      window.removeEventListener("scroll", updateMenuPosition, true);
    };
  }, [open, placement]);

  const openAndFocusOption = (index: number) => {
    setOpen(true);
    window.requestAnimationFrame(() => {
      const optionButtons = menuRef.current?.querySelectorAll<HTMLButtonElement>('[role="option"]');
      optionButtons?.[Math.max(0, Math.min(index, options.length - 1))]?.focus();
    });
  };

  const handleTriggerKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {
    if (event.key !== "ArrowDown" && event.key !== "ArrowUp" && event.key !== "Home" && event.key !== "End") return;
    event.preventDefault();
    const selectedIndex = Math.max(0, options.findIndex((option) => option.value === value));
    openAndFocusOption(event.key === "End" ? options.length - 1 : event.key === "Home" ? 0 : selectedIndex);
  };

  return (
    <div ref={rootRef} className={`relative min-w-0 ${placement === "inline" ? "w-full" : ""}`}>
      <button
        ref={buttonRef}
        type="button"
        className={`inline-flex h-10 min-w-32 max-w-full items-center rounded-full text-base text-white/85 transition hover:bg-white/10 hover:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-white/30 ${placement === "inline" ? "w-full px-4" : ""}`}
        aria-label={props["aria-label"]}
        aria-haspopup="listbox"
        aria-expanded={open}
        aria-controls={listboxId}
        onKeyDown={handleTriggerKeyDown}
        onClick={() => setOpen((current) => !current)}
      >
        {placement === "inline" && label ? <span className="shrink-0 pr-3 text-sm font-medium text-white/55">{label}</span> : null}
        <span className={`min-w-0 flex-1 truncate text-left ${placement === "inline" ? "" : "px-4"}`}>{selectedOption?.label}</span>
        <span className="grid size-10 flex-none place-items-center text-white/60">
          <CaretDown size={18} weight="bold" />
        </span>
      </button>
      {open && placement === "inline" ? (
        <SelectOptions
          menuRef={menuRef}
          id={listboxId}
          options={options}
          value={value}
          onValueChange={onValueChange}
          setOpen={setOpen}
          className="subtle-scrollbar mt-2 max-h-56 w-full overflow-auto rounded-[1.25rem] bg-white/[0.04] p-1 text-white ring-1 ring-white/10"
          aria-label={props["aria-label"]}
        />
      ) : open ? (
        createPortal(
          <SelectOptions
            menuRef={menuRef}
            id={listboxId}
            options={options}
            value={value}
            onValueChange={onValueChange}
            setOpen={setOpen}
            className="subtle-scrollbar z-50 overflow-auto rounded-[1.5rem] bg-slate-950/90 p-1 text-white shadow-2xl ring-1 ring-white/10 backdrop-blur-xl"
            style={menuStyle}
            aria-label={props["aria-label"]}
          />,
          document.body,
        )
      ) : null}
    </div>
  );
}

type SelectOptionsProps<TValue extends string> = {
  menuRef: RefObject<HTMLDivElement | null>;
  id: string;
  options: readonly BottomControlSelectOption<TValue>[];
  value: TValue;
  className: string;
  style?: CSSProperties;
  "aria-label": string;
  onValueChange: (value: TValue) => void;
  setOpen: (open: boolean) => void;
};

function SelectOptions<TValue extends string>({ menuRef, id, options, value, className, style, onValueChange, setOpen, ...props }: SelectOptionsProps<TValue>) {
  const handleOptionKeyDown = (event: KeyboardEvent<HTMLButtonElement>, index: number) => {
    const optionButtons = menuRef.current?.querySelectorAll<HTMLButtonElement>('[role="option"]');
    if (!optionButtons) return;
    if (event.key === "Escape") {
      event.preventDefault();
      setOpen(false);
      return;
    }
    const nextIndex = event.key === "ArrowDown" ? (index + 1) % options.length
      : event.key === "ArrowUp" ? (index - 1 + options.length) % options.length
      : event.key === "Home" ? 0
      : event.key === "End" ? options.length - 1
      : undefined;
    if (nextIndex === undefined) return;
    event.preventDefault();
    optionButtons[nextIndex]?.focus();
  };

  return (
    <div ref={menuRef} id={id} className={className} style={style} role="listbox" aria-label={props["aria-label"]}>
      {options.map((option, index) => {
        const selected = option.value === value;
        return (
          <button
            key={option.value}
            type="button"
            className={`flex h-10 w-full items-center gap-3 rounded-full px-3 text-left text-sm transition ${selected ? "bg-white !text-black" : "text-white/80 hover:bg-white/10 hover:text-white"}`}
            role="option"
            aria-selected={selected}
            tabIndex={selected ? 0 : -1}
            onKeyDown={(event) => handleOptionKeyDown(event, index)}
            onClick={() => {
              onValueChange(option.value);
              setOpen(false);
            }}
          >
            <span className="grid size-5 place-items-center">{selected ? <Check size={16} weight="bold" /> : null}</span>
            <span className="min-w-0 flex-1 truncate">{option.label}</span>
          </button>
        );
      })}
    </div>
  );
}
