import {
  focusDialogOnOpen,
  handleDialogKeyDown,
  restoreDialogFocus,
  type DialogKeyboardEvent
} from "./dialogInteractions";

export type DialogStackId = symbol;

export type DialogStackRegistration = {
  id: DialogStackId;
  activationOrder: number;
  panel: HTMLElement;
  restoreFocus: HTMLElement | null;
  canClose: () => boolean;
  onClose?: () => void;
};

type DialogStackEntry = DialogStackRegistration & {
  restoreTargets: HTMLElement[];
};

const openDialogs: DialogStackEntry[] = [];
let activationSequence = 0;
let listeningForKeyDown = false;

function topDialog(): DialogStackEntry | null {
  return openDialogs[openDialogs.length - 1] ?? null;
}

function currentActiveElement(): Element | null {
  return typeof document === "undefined" ? null : document.activeElement;
}

function appendUniqueTargets(targets: HTMLElement[], additions: Array<HTMLElement | null>): HTMLElement[] {
  const next = [...targets];
  additions.forEach((target) => {
    if (target && !next.includes(target)) next.push(target);
  });
  return next;
}

function exposeAsTopmost(panel: HTMLElement): void {
  panel.removeAttribute("inert");
  panel.removeAttribute("aria-hidden");
  panel.setAttribute("aria-modal", "true");
  panel.setAttribute("data-dialog-stack-state", "topmost");
}

function hideAsUnderlying(panel: HTMLElement): void {
  panel.setAttribute("inert", "");
  panel.setAttribute("aria-hidden", "true");
  panel.removeAttribute("aria-modal");
  panel.setAttribute("data-dialog-stack-state", "underlying");
}

function syncStackAccessibility(): void {
  const topIndex = openDialogs.length - 1;
  openDialogs.forEach((entry, index) => {
    if (index === topIndex) exposeAsTopmost(entry.panel);
    else hideAsUnderlying(entry.panel);
  });
}

function handleWindowKeyDown(event: KeyboardEvent): void {
  handleTopDialogKeyDown(event, currentActiveElement());
}

function syncWindowKeyDownListener(): void {
  if (typeof window === "undefined") return;
  if (openDialogs.length > 0 && !listeningForKeyDown) {
    window.addEventListener("keydown", handleWindowKeyDown);
    listeningForKeyDown = true;
  } else if (openDialogs.length === 0 && listeningForKeyDown) {
    window.removeEventListener("keydown", handleWindowKeyDown);
    listeningForKeyDown = false;
  }
}

function focusAfterTopmostCloses(entry: DialogStackEntry): void {
  const nextTop = topDialog();
  if (nextTop) {
    const targetInsideNextDialog = entry.restoreTargets.find(
      (target) => target.isConnected !== false && nextTop.panel.contains(target)
    );
    if (targetInsideNextDialog && restoreDialogFocus(targetInsideNextDialog)) return;
    focusDialogOnOpen(nextTop.panel, currentActiveElement());
    return;
  }

  entry.restoreTargets.some((target) => restoreDialogFocus(target));
}

function unregisterDialog(id: DialogStackId): void {
  const index = openDialogs.findIndex((entry) => entry.id === id);
  if (index < 0) return;

  const wasTopmost = index === openDialogs.length - 1;
  const [entry] = openDialogs.splice(index, 1);
  exposeAsTopmost(entry.panel);
  syncStackAccessibility();
  syncWindowKeyDownListener();

  if (wasTopmost) focusAfterTopmostCloses(entry);
}

export function nextDialogActivationOrder(): number {
  activationSequence += 1;
  return activationSequence;
}

export function registerDialog(
  registration: DialogStackRegistration,
  activeElement: Element | null
): () => void {
  const existingIndex = openDialogs.findIndex((entry) => entry.id === registration.id);
  if (existingIndex >= 0) openDialogs.splice(existingIndex, 1);

  const entry: DialogStackEntry = {
    ...registration,
    restoreTargets: appendUniqueTargets([], [registration.restoreFocus])
  };
  openDialogs.push(entry);
  openDialogs.sort((left, right) => left.activationOrder - right.activationOrder);

  const entryIndex = openDialogs.indexOf(entry);
  const entryBelow = openDialogs[entryIndex - 1];
  if (entryBelow) entry.restoreTargets = appendUniqueTargets(entry.restoreTargets, entryBelow.restoreTargets);
  for (let index = entryIndex + 1; index < openDialogs.length; index += 1) {
    openDialogs[index].restoreTargets = appendUniqueTargets(openDialogs[index].restoreTargets, entry.restoreTargets);
  }

  if (topDialog() === entry) focusDialogOnOpen(entry.panel, activeElement);
  syncStackAccessibility();
  syncWindowKeyDownListener();

  return () => unregisterDialog(registration.id);
}

export function handleTopDialogKeyDown(
  event: DialogKeyboardEvent,
  activeElement: Element | null
): boolean {
  const entry = topDialog();
  if (!entry) return false;
  return handleDialogKeyDown(entry.panel, event, activeElement, entry.canClose(), entry.onClose);
}

export function dialogIsTopmost(id: DialogStackId): boolean {
  const entry = topDialog();
  return !entry || entry.id === id;
}

export function resetDialogStackForTests(): void {
  openDialogs.splice(0).forEach((entry) => exposeAsTopmost(entry.panel));
  activationSequence = 0;
  syncWindowKeyDownListener();
}
