import { useEffect, useRef } from "react";

// R8 S3 — one shared registry of open "dismissable layers" (modals, popovers, players), ordered by
// open time. It replaces the three mutually-unaware Escape systems that each guessed at ordering with
// its own `stopPropagation`: Modal's private `modalStack`, the popovers' document listeners, and the
// two players' `modalCount()` checks. Now EVERY layer keeps its own key handler but coordinates
// through this one stack — a layer acts on Escape only when it is topmost, and reads `anyAbove()` to
// know whether to defer. Because the decision is a stack lookup, not event-propagation order, the fix
// is robust to which listener happens to run first (the old stopPropagation was not).
//
// Notably this fixes the documented PlexPlayer bug: its menus register here, so the player sees a menu
// as a layer ABOVE it and defers, letting the menu's own Escape close it first — the player's
// auto-skip-cancel Escape then lands on the next press, instead of being swallowed by a blanket
// stopPropagation. (Popover-vs-popover ordering is still floating-ui's <FloatingTree>; this stack adds
// the cross-TYPE ordering the tree can't see.)

let stack: number[] = [];
let nextId = 1;

export function pushLayer(): number {
  const id = nextId++;
  stack.push(id);
  return id;
}

export function removeLayer(id: number): void {
  stack = stack.filter((x) => x !== id);
}

export function isTopmost(id: number): boolean {
  return stack.length > 0 && stack[stack.length - 1] === id;
}

/** True when at least one layer sits above `id` — the "should I defer?" predicate for a layer with a
 *  richer key handler than a plain close (the players). */
export function anyLayerAbove(id: number): boolean {
  const i = stack.indexOf(id);
  return i !== -1 && i < stack.length - 1;
}

/** Register an open layer for as long as `active` is true, and get live predicates to read inside your
 *  own key handler. Pass `onEscape` to also install a topmost-only Escape listener (the common case —
 *  modals, popovers). Omit it for a layer that owns a bigger key handler and only needs `anyAbove()`
 *  to decide whether to defer (the players). */
export function useLayer(
  active: boolean,
  onEscape?: () => void,
): { isTopmost: () => boolean; anyAbove: () => boolean } {
  const idRef = useRef<number | null>(null);
  const escRef = useRef(onEscape);
  escRef.current = onEscape;

  useEffect(() => {
    if (!active) return;
    const id = pushLayer();
    idRef.current = id;
    let onKey: ((e: KeyboardEvent) => void) | undefined;
    if (escRef.current) {
      onKey = (e: KeyboardEvent) => {
        if (e.key === "Escape" && isTopmost(id)) escRef.current?.();
      };
      document.addEventListener("keydown", onKey);
    }
    return () => {
      if (onKey) document.removeEventListener("keydown", onKey);
      removeLayer(id);
      idRef.current = null;
    };
    // Re-register only when open/closed toggles; onEscape is read fresh via escRef.
  }, [active]);

  return {
    isTopmost: () => idRef.current != null && isTopmost(idRef.current),
    anyAbove: () => idRef.current != null && anyLayerAbove(idRef.current),
  };
}
