import { computeFloatingPosition, type FloatingPlacement } from "@nodedc/ui-core";

export interface FloatingLayerOptions {
  trigger: HTMLElement;
  surface: HTMLElement;
  placement?: FloatingPlacement;
  offset?: number;
  minWidth?: number;
  matchTriggerWidth?: boolean;
  closeOnSelect?: boolean;
  onOpenChange?: (open: boolean) => void;
}

export interface FloatingLayerController {
  open: () => void;
  close: () => void;
  toggle: () => void;
  reposition: () => void;
  isOpen: () => boolean;
  destroy: () => void;
}

export function createFloatingLayer({
  trigger,
  surface,
  placement = "bottom-start",
  offset = 8,
  minWidth = 180,
  matchTriggerWidth = true,
  closeOnSelect = false,
  onOpenChange,
}: FloatingLayerOptions): FloatingLayerController {
  let openState = false;
  const originalParent = surface.parentNode;
  const originalNextSibling = surface.nextSibling;

  surface.hidden = true;
  surface.classList.add("nodedc-dropdown-surface");
  document.body.append(surface);

  const reposition = () => {
    if (!openState) return;
    const anchor = trigger.getBoundingClientRect();
    const measured = surface.getBoundingClientRect();
    const surfaceWidth = Math.max(minWidth, matchTriggerWidth ? anchor.width : 0, measured.width);
    const surfaceHeight = Math.max(1, surface.scrollHeight);
    const position = computeFloatingPosition({ anchor, surfaceWidth, surfaceHeight, placement, offset });
    Object.assign(surface.style, {
      position: "fixed",
      top: `${position.top}px`,
      left: `${position.left}px`,
      width: `${surfaceWidth}px`,
      maxHeight: `${position.maxHeight}px`,
      visibility: "visible",
    });
  };

  const open = () => {
    if (openState || trigger.hasAttribute("disabled")) return;
    openState = true;
    surface.hidden = false;
    trigger.setAttribute("aria-expanded", "true");
    window.dispatchEvent(new CustomEvent("nodedc-floating-open", { detail: { surface } }));
    requestAnimationFrame(reposition);
    onOpenChange?.(true);
  };

  const close = () => {
    if (!openState) return;
    openState = false;
    surface.hidden = true;
    trigger.setAttribute("aria-expanded", "false");
    onOpenChange?.(false);
  };

  const toggle = () => {
    if (openState) close();
    else open();
  };

  const handleTriggerClick = (event: MouseEvent) => {
    event.preventDefault();
    toggle();
  };
  const handleDocumentPointerDown = (event: PointerEvent) => {
    if (!(event.target instanceof Node)) return;
    if (!trigger.contains(event.target) && !surface.contains(event.target)) close();
  };
  const handleKeyDown = (event: KeyboardEvent) => {
    if (event.key === "Escape" && openState) {
      close();
      trigger.focus();
    }
  };
  const handleOtherOpen = (event: Event) => {
    const otherSurface = (event as CustomEvent<{ surface?: HTMLElement }>).detail.surface;
    if (otherSurface && otherSurface !== surface) close();
  };
  const handleSurfaceClick = (event: MouseEvent) => {
    if (closeOnSelect && event.target instanceof HTMLElement && event.target.closest("[data-nodedc-option]")) close();
  };

  trigger.addEventListener("click", handleTriggerClick);
  surface.addEventListener("click", handleSurfaceClick);
  document.addEventListener("pointerdown", handleDocumentPointerDown);
  document.addEventListener("keydown", handleKeyDown);
  window.addEventListener("nodedc-floating-open", handleOtherOpen as EventListener);
  window.addEventListener("resize", reposition);
  window.addEventListener("scroll", reposition, true);

  return {
    open,
    close,
    toggle,
    reposition,
    isOpen: () => openState,
    destroy: () => {
      close();
      trigger.removeEventListener("click", handleTriggerClick);
      surface.removeEventListener("click", handleSurfaceClick);
      document.removeEventListener("pointerdown", handleDocumentPointerDown);
      document.removeEventListener("keydown", handleKeyDown);
      window.removeEventListener("nodedc-floating-open", handleOtherOpen as EventListener);
      window.removeEventListener("resize", reposition);
      window.removeEventListener("scroll", reposition, true);
      if (originalParent) originalParent.insertBefore(surface, originalNextSibling);
      surface.removeAttribute("style");
    },
  };
}

