// @vitest-environment happy-dom
//
// Every modal built on AppModal shows an X in the corner, and Escape must do
// exactly what that X does. Each consumer already wires onClose to the safe
// direction (TrustServerModal -> onDecline, DeviceLimitModal ->
// cancelDeviceLogin), so mirroring the X is never the dangerous choice.
//
// The listener is bubble-phase on purpose: FileDialogs' DialogShell registers
// its Escape handler in the CAPTURE phase and stops propagation, so a nested
// confirm/prompt dialog swallows Escape and closes itself rather than the
// modal underneath it.
import { act } from 'react';
import { createRoot } from 'react-dom/client';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { AppModal } from './AppModal';

let container;
let currentRoot;

beforeEach(() => {
  container = document.createElement('div');
  document.body.appendChild(container);
});

afterEach(() => {
  // Unmount, not just detach: the Escape listener is on `document`, so a
  // leaked root would keep listening across tests.
  if (currentRoot) act(() => currentRoot.unmount());
  currentRoot = null;
  document.body.removeChild(container);
  container = null;
});

function render(props) {
  const root = createRoot(container);
  currentRoot = root;
  act(() =>
    root.render(
      <AppModal title="Help" icon="assets/help.svg" onClose={() => {}} {...props}>
        <p>body</p>
      </AppModal>
    )
  );
  return root;
}

function pressEscape() {
  act(() => {
    document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
  });
}

describe('AppModal', () => {
  it('closes on Escape, like the X', () => {
    const onClose = vi.fn();
    render({ onClose });
    pressEscape();
    expect(onClose).toHaveBeenCalledTimes(1);
  });

  it('closes on the X button', () => {
    const onClose = vi.fn();
    render({ onClose });
    act(() => container.querySelector('.app-modal-close').click());
    expect(onClose).toHaveBeenCalledTimes(1);
  });

  it('ignores other keys', () => {
    const onClose = vi.fn();
    render({ onClose });
    act(() => {
      document.dispatchEvent(new KeyboardEvent('keydown', { key: 'a', bubbles: true }));
      document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
    });
    expect(onClose).not.toHaveBeenCalled();
  });

  it('stops listening once unmounted', () => {
    const onClose = vi.fn();
    const root = render({ onClose });
    act(() => root.unmount());
    currentRoot = null; // already unmounted; don't let afterEach do it twice
    pressEscape();
    expect(onClose).not.toHaveBeenCalled();
  });

  it('appends an optional className to the modal', () => {
    render({ className: 'app-modal--help' });
    const modal = container.querySelector('.app-modal');
    expect(modal.className).toBe('app-modal app-modal--help');
  });
});
