function assert(condition: unknown, message = "assertion failed"): void {
  if (!condition) throw new Error(message);
}

function assertEqual<T>(actual: T, expected: T, message = "values should be equal"): void {
  if (actual !== expected) throw new Error(`${message}: expected ${String(expected)}, got ${String(actual)}`);
}

import { renderToStaticMarkup } from "react-dom/server";
import Dialog from "../src/components/Dialog";
import {
  focusDialogOnOpen,
  handleDialogKeyDown,
  restoreDialogFocus,
  shouldCloseDialogOnBackdrop,
  trapDialogFocus
} from "../src/components/dialogInteractions";
import {
  handleTopDialogKeyDown,
  nextDialogActivationOrder,
  registerDialog,
  resetDialogStackForTests
} from "../src/components/dialogStack";

type FocusableFixture = HTMLElement & { focusCount: number; attributes: Map<string, string> };

function focusableFixture({ connected = true, hidden = false }: { connected?: boolean; hidden?: boolean } = {}): FocusableFixture {
  const attributes = new Map<string, string>();
  if (hidden) attributes.set("hidden", "");
  const fixture = {
    attributes,
    focusCount: 0,
    tabIndex: 0,
    isConnected: connected,
    getAttribute: (name: string) => attributes.get(name) ?? null,
    hasAttribute: (name: string) => attributes.has(name),
    setAttribute: (name: string, value: string) => attributes.set(name, value),
    removeAttribute: (name: string) => attributes.delete(name),
    closest: () => attributes.has("hidden") || attributes.has("inert") || attributes.get("aria-hidden") === "true" ? fixture : null,
    focus() {
      fixture.focusCount += 1;
    }
  };
  return fixture as unknown as FocusableFixture;
}

function panelFixture(elements: FocusableFixture[], autofocus?: FocusableFixture) {
  const panel = focusableFixture();
  panel.tabIndex = -1;
  Object.assign(panel, {
    contains: (element: Element | null) => element === panel || elements.includes(element as FocusableFixture),
    querySelectorAll: () => elements,
    querySelector: (selector: string) => selector === "[autofocus]" ? (autofocus ?? null) : null
  });
  return panel;
}

function keyboardEvent(key: string, shiftKey = false) {
  const fixture = {
    key,
    shiftKey,
    prevented: 0,
    preventDefault() {
      fixture.prevented += 1;
    }
  };
  return fixture;
}

const markup = renderToStaticMarkup(
  <Dialog open title="Dialog title" onClose={() => undefined}>
    <button type="button">First action</button>
  </Dialog>
);
assert(markup.includes('role="dialog"'), "dialog semantics are preserved");
assert(markup.includes('aria-modal="true"'), "modal semantics are preserved");
assert(markup.includes('data-dialog-stack-state="topmost"'), "dialog exposes its initial stack state");
assert(markup.includes('tabindex="-1"'), "dialog panel can receive fallback focus");

const first = focusableFixture();
const requested = focusableFixture();
const entryPanel = panelFixture([first, requested], requested);
assertEqual(focusDialogOnOpen(entryPanel, null), requested, "autofocus target receives initial focus");
assertEqual(requested.focusCount, 1, "autofocus target is focused once");

const defaultEntryPanel = panelFixture([first]);
assertEqual(focusDialogOnOpen(defaultEntryPanel, null), first, "first available control receives initial focus");
assertEqual(first.focusCount, 1, "first available control is focused once");

const retained = focusableFixture();
const retainedPanel = panelFixture([retained]);
assertEqual(focusDialogOnOpen(retainedPanel, retained), retained, "existing focus inside the dialog is retained");
assertEqual(retained.focusCount, 0, "retained focus is not moved redundantly");

const emptyPanel = panelFixture([]);
assertEqual(focusDialogOnOpen(emptyPanel, null), emptyPanel, "panel receives focus when no control is available");
assertEqual(emptyPanel.focusCount, 1, "fallback panel is focused");

const boundaryFirst = focusableFixture();
const boundaryMiddle = focusableFixture();
const boundaryLast = focusableFixture();
const boundaryPanel = panelFixture([boundaryFirst, boundaryMiddle, boundaryLast]);

const forwardWrap = keyboardEvent("Tab");
assert(trapDialogFocus(boundaryPanel, forwardWrap, boundaryLast), "forward Tab wraps at the last control");
assertEqual(forwardWrap.prevented, 1, "forward wrap prevents focus from leaving the dialog");
assertEqual(boundaryFirst.focusCount, 1, "forward wrap focuses the first control");

const backwardWrap = keyboardEvent("Tab", true);
assert(trapDialogFocus(boundaryPanel, backwardWrap, boundaryFirst), "Shift+Tab wraps at the first control");
assertEqual(backwardWrap.prevented, 1, "backward wrap prevents focus from leaving the dialog");
assertEqual(boundaryLast.focusCount, 1, "backward wrap focuses the last control");

const interiorTab = keyboardEvent("Tab");
assert(!trapDialogFocus(boundaryPanel, interiorTab, boundaryMiddle), "interior Tab keeps native focus order");
assertEqual(interiorTab.prevented, 0, "interior Tab is not prevented");

const outsideTab = keyboardEvent("Tab");
assert(trapDialogFocus(boundaryPanel, outsideTab, focusableFixture()), "focus cannot enter the modal from outside its boundary");
assertEqual(boundaryFirst.focusCount, 2, "outside focus is redirected to the first control");

let closeCount = 0;
const escape = keyboardEvent("Escape");
assert(handleDialogKeyDown(boundaryPanel, escape, boundaryFirst, true, () => { closeCount += 1; }), "Escape closes a closable dialog");
assertEqual(closeCount, 1, "Escape invokes onClose once");
assertEqual(escape.prevented, 0, "Escape preserves the existing default-event behavior");

const blockedEscape = keyboardEvent("Escape");
assert(!handleDialogKeyDown(boundaryPanel, blockedEscape, boundaryFirst, false, () => { closeCount += 1; }), "Escape is ignored while closing is disabled");
assertEqual(closeCount, 1, "disabled Escape does not invoke onClose");

const opener = focusableFixture();
assert(restoreDialogFocus(opener), "connected opener focus is restored");
assertEqual(opener.focusCount, 1, "opener receives restored focus once");
assert(!restoreDialogFocus(focusableFixture({ connected: false })), "removed opener is not focused");

const backdrop = {} as EventTarget;
const child = {} as EventTarget;
assert(shouldCloseDialogOnBackdrop(backdrop, backdrop, true, true), "direct backdrop press closes a closable dialog");
assert(!shouldCloseDialogOnBackdrop(child, backdrop, true, true), "presses inside the panel do not close the dialog");
assert(!shouldCloseDialogOnBackdrop(backdrop, backdrop, false, true), "backdrop closing can be disabled");
assert(!shouldCloseDialogOnBackdrop(backdrop, backdrop, true, false), "backdrop press respects closeDisabled");

resetDialogStackForTests();
const outerOpener = focusableFixture();
const parentOpener = focusableFixture();
const parentLast = focusableFixture();
const parentPanel = panelFixture([parentOpener, parentLast]);
const childFirst = focusableFixture();
const childLast = focusableFixture();
const childPanel = panelFixture([childFirst, childLast]);
const parentId = Symbol("parent-dialog");
const childId = Symbol("child-dialog");
const parentOrder = nextDialogActivationOrder();
const childOrder = nextDialogActivationOrder();
let parentCloseCount = 0;
let childCloseCount = 0;
let childCanClose = false;

// React may commit a nested child's effect before its parent effect. Activation
// order is allocated during render so the child must remain topmost either way.
const unregisterChild = registerDialog({
  id: childId,
  activationOrder: childOrder,
  panel: childPanel,
  restoreFocus: parentOpener,
  canClose: () => childCanClose,
  onClose: () => { childCloseCount += 1; }
}, parentOpener);
const unregisterParent = registerDialog({
  id: parentId,
  activationOrder: parentOrder,
  panel: parentPanel,
  restoreFocus: outerOpener,
  canClose: () => true,
  onClose: () => { parentCloseCount += 1; }
}, childFirst);

assertEqual(parentPanel.getAttribute("data-dialog-stack-state"), "underlying", "parent dialog is marked as underlying");
assert(parentPanel.hasAttribute("inert"), "parent dialog is inert while a child is open");
assertEqual(parentPanel.getAttribute("aria-hidden"), "true", "parent dialog is hidden from assistive technology");
assertEqual(parentPanel.getAttribute("aria-modal"), null, "underlying dialog no longer claims modal semantics");
assertEqual(childPanel.getAttribute("data-dialog-stack-state"), "topmost", "child dialog is marked as topmost");
assertEqual(childPanel.getAttribute("aria-modal"), "true", "child dialog owns modal semantics");

const stackedTab = keyboardEvent("Tab");
const parentFocusBeforeTab = parentOpener.focusCount;
assert(handleTopDialogKeyDown(stackedTab, childLast), "topmost dialog traps Tab");
assertEqual(stackedTab.prevented, 1, "stacked Tab wrapping is prevented once");
assertEqual(parentOpener.focusCount, parentFocusBeforeTab, "underlying dialog does not process Tab");

const disabledStackedEscape = keyboardEvent("Escape");
assert(!handleTopDialogKeyDown(disabledStackedEscape, childFirst), "disabled child ignores Escape without falling through");
assertEqual(childCloseCount, 0, "disabled child remains open");
assertEqual(parentCloseCount, 0, "disabled child Escape does not close the parent");

childCanClose = true;
const stackedEscape = keyboardEvent("Escape");
assert(handleTopDialogKeyDown(stackedEscape, childFirst), "topmost dialog handles Escape");
assertEqual(childCloseCount, 1, "one Escape closes the child once");
assertEqual(parentCloseCount, 0, "one Escape does not close the parent");

const parentFocusBeforeChildClose = parentOpener.focusCount;
unregisterChild();
assertEqual(parentOpener.focusCount, parentFocusBeforeChildClose + 1, "closing the child restores focus inside the parent");
assert(!parentPanel.hasAttribute("inert"), "parent dialog becomes interactive after the child closes");
assertEqual(parentPanel.getAttribute("aria-hidden"), null, "parent dialog returns to the accessibility tree");
assertEqual(parentPanel.getAttribute("aria-modal"), "true", "parent dialog regains modal semantics");
assertEqual(parentPanel.getAttribute("data-dialog-stack-state"), "topmost", "parent dialog becomes topmost");

const parentEscape = keyboardEvent("Escape");
assert(handleTopDialogKeyDown(parentEscape, parentOpener), "parent handles Escape after child cleanup");
assertEqual(parentCloseCount, 1, "parent closes only on its own Escape handling");
unregisterParent();
assertEqual(outerOpener.focusCount, 1, "closing the final dialog restores the outer opener");
assert(!handleTopDialogKeyDown(keyboardEvent("Escape"), outerOpener), "keyboard handling stops when the stack is empty");

resetDialogStackForTests();
const forcedOuterOpener = focusableFixture();
const removedParentOpener = focusableFixture();
const removedParentPanel = panelFixture([removedParentOpener]);
const forcedChildControl = focusableFixture();
const forcedChildPanel = panelFixture([forcedChildControl]);
const unregisterForcedParent = registerDialog({
  id: Symbol("forced-parent-dialog"),
  activationOrder: nextDialogActivationOrder(),
  panel: removedParentPanel,
  restoreFocus: forcedOuterOpener,
  canClose: () => true
}, forcedOuterOpener);
const unregisterForcedChild = registerDialog({
  id: Symbol("forced-child-dialog"),
  activationOrder: nextDialogActivationOrder(),
  panel: forcedChildPanel,
  restoreFocus: removedParentOpener,
  canClose: () => true
}, removedParentOpener);
unregisterForcedParent();
Object.assign(removedParentOpener, { isConnected: false });
unregisterForcedChild();
assertEqual(forcedOuterOpener.focusCount, 1, "removing a dialog tree restores the nearest connected outer opener");

resetDialogStackForTests();
const strictModeOpener = focusableFixture();
const strictModeControl = focusableFixture();
const strictModePanel = panelFixture([strictModeControl]);
const strictModeRegistration = {
  id: Symbol("strict-mode-dialog"),
  activationOrder: nextDialogActivationOrder(),
  panel: strictModePanel,
  restoreFocus: strictModeOpener,
  canClose: () => true
};
const unregisterStrictModeFirstPass = registerDialog(strictModeRegistration, strictModeOpener);
unregisterStrictModeFirstPass();
const unregisterStrictModeReplay = registerDialog(strictModeRegistration, strictModeOpener);
assertEqual(strictModeControl.focusCount, 2, "StrictMode effect replay returns focus to the dialog");
assertEqual(strictModeOpener.focusCount, 1, "StrictMode cleanup restores the opener before replay");
unregisterStrictModeReplay();
assertEqual(strictModeOpener.focusCount, 2, "StrictMode replay cleanup restores focus without leaving a stack entry");
assert(!handleTopDialogKeyDown(keyboardEvent("Escape"), strictModeOpener), "StrictMode replay leaves no stale topmost dialog");

resetDialogStackForTests();
let keydownListenerAdds = 0;
let keydownListenerRemovals = 0;
Object.defineProperty(globalThis, "window", {
  configurable: true,
  value: {
    addEventListener(type: string) {
      if (type === "keydown") keydownListenerAdds += 1;
    },
    removeEventListener(type: string) {
      if (type === "keydown") keydownListenerRemovals += 1;
    },
    getComputedStyle() {
      return { display: "block", visibility: "visible" };
    }
  }
});
try {
  const listenerOuter = focusableFixture();
  const listenerParentPanel = panelFixture([focusableFixture()]);
  const listenerChildPanel = panelFixture([focusableFixture()]);
  const unregisterListenerParent = registerDialog({
    id: Symbol("listener-parent"),
    activationOrder: nextDialogActivationOrder(),
    panel: listenerParentPanel,
    restoreFocus: listenerOuter,
    canClose: () => true
  }, listenerOuter);
  const unregisterListenerChild = registerDialog({
    id: Symbol("listener-child"),
    activationOrder: nextDialogActivationOrder(),
    panel: listenerChildPanel,
    restoreFocus: listenerParentPanel,
    canClose: () => true
  }, listenerParentPanel);
  assertEqual(keydownListenerAdds, 1, "one window keyboard listener serves the whole dialog stack");
  unregisterListenerChild();
  assertEqual(keydownListenerRemovals, 0, "keyboard listener remains while a parent dialog is open");
  unregisterListenerParent();
  assertEqual(keydownListenerRemovals, 1, "keyboard listener is removed after the final dialog closes");
} finally {
  resetDialogStackForTests();
  Reflect.deleteProperty(globalThis, "window");
}
