import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { createOfficeScreenPanel } from "../media/officeScreenPanel.ts";
import type { MediaSurfaceDescriptor } from "../media/presentation.ts";

type Listener = (event: FakeEvent) => void;
class FakeEvent {
  defaultPrevented = false;
  readonly type: string;
  readonly target: FakeElement;
  readonly key: string;
  readonly shiftKey: boolean;
  constructor(
    type: string,
    target: FakeElement,
    key = "",
    shiftKey = false,
  ) {
    this.type = type;
    this.target = target;
    this.key = key;
    this.shiftKey = shiftKey;
  }
  preventDefault(): void { this.defaultPrevented = true; }
}
class FakeElement {
  readonly children: FakeElement[] = [];
  readonly attributes = new Map<string, string>();
  readonly listeners = new Map<string, Listener[]>();
  parentElement: FakeElement | null = null;
  className = "";
  textContent = "";
  hidden = false;
  disabled = false;
  type = "";
  id = "";
  readonly ownerDocument: FakeDocument;
  readonly tagName: string;
  constructor(ownerDocument: FakeDocument, tagName: string) {
    this.ownerDocument = ownerDocument;
    this.tagName = tagName;
  }
  append(...nodes: FakeElement[]): void { for (const node of nodes) { node.parentElement = this; this.children.push(node); } }
  setAttribute(name: string, value: string): void { this.attributes.set(name, value); }
  getAttribute(name: string): string | null { return this.attributes.get(name) ?? null; }
  addEventListener(type: string, listener: Listener): void {
    const list = this.listeners.get(type); if (list) list.push(listener); else this.listeners.set(type, [listener]);
  }
  dispatch(type: string, init: { key?: string; shiftKey?: boolean } = {}): FakeEvent {
    const event = new FakeEvent(type, this, init.key, init.shiftKey);
    for (const listener of this.listeners.get(type) ?? []) listener(event);
    return event;
  }
  focus(): void { this.ownerDocument.activeElement = this; }
  remove(): void {
    if (!this.parentElement) return;
    const index = this.parentElement.children.indexOf(this); if (index >= 0) this.parentElement.children.splice(index, 1);
    this.parentElement = null;
  }
  find(attribute: string, value: string): FakeElement {
    if (this.attributes.get(attribute) === value) return this;
    for (const child of this.children) { try { return child.find(attribute, value); } catch { /* next */ } }
    throw new Error(`missing [${attribute}=${value}]`);
  }
  text(): string { return this.textContent + this.children.map((child) => child.text()).join(""); }
}
class FakeDocument {
  activeElement: FakeElement | null = null;
  createElement(tag: string): FakeElement { return new FakeElement(this, tag.toUpperCase()); }
}

const SURFACES: MediaSurfaceDescriptor[] = [
  { screenId: "lobby-monitor", officeId: "hq", levelId: "level-1", roomId: "Lobby", kind: "tera:screen.monitor", bound: false },
  { screenId: "commons-display", officeId: "hq", levelId: "level-2", roomId: "Commons", kind: "tera:screen.wall-display", bound: true },
];

function setup() {
  const document = new FakeDocument();
  const container = document.createElement("div");
  const selected: string[] = [];
  const shares: string[] = [];
  const stops: string[] = [];
  const opts: [string, boolean][] = [];
  const panel = createOfficeScreenPanel({
    container: container as unknown as HTMLElement,
    surfaces: SURFACES,
    onSelect: (surface) => selected.push(surface.screenId),
    onRequestShare: (surface) => shares.push(surface.screenId),
    onStopShare: (surface) => stops.push(surface.screenId),
    onViewerOptIn: (surface, value) => opts.push([surface.screenId, value]),
  });
  return { document, container, panel, selected, shares, stops, opts };
}

describe("office screen manager panel", () => {
  it("builds a closed labelled dialog with safe screen, room, and status text", () => {
    const { container, panel } = setup();
    const root = panel.root as unknown as FakeElement;
    assert.equal(container.children.length, 1);
    assert.equal(root.hidden, true);
    assert.equal(root.getAttribute("role"), "dialog");
    assert.equal(root.getAttribute("aria-modal"), "true");
    assert.match(root.text(), /lobby-monitor/);
    assert.match(root.text(), /Lobby/);
    assert.match(root.text(), /Media off/);
    assert.match(root.text(), /Media active/);
  });

  it("selects, explicitly opts in, and emits share/stop intent without capture", () => {
    const { panel, selected, shares, stops, opts } = setup();
    panel.open();
    const root = panel.root as unknown as FakeElement;
    root.find("data-screen-id", "commons-display").dispatch("click");
    root.find("data-action", "opt-in").dispatch("click");
    root.find("data-action", "share").dispatch("click");
    root.find("data-action", "stop").dispatch("click");
    assert.deepEqual(selected, ["commons-display"]);
    assert.deepEqual(opts, [["commons-display", true]]);
    assert.deepEqual(shares, ["commons-display"]);
    assert.deepEqual(stops, ["commons-display"]);
    assert.deepEqual(panel.state().optedInScreenIds, ["commons-display"]);
    assert.equal("mediaDevices" in panel, false);
  });

  it("keeps presenting separate from viewer opt-in", () => {
    const { panel, shares, opts } = setup();
    panel.open();
    const root = panel.root as unknown as FakeElement;
    const share = root.find("data-action", "share");
    assert.equal(share.disabled, false);
    share.dispatch("click");
    assert.deepEqual(shares, ["lobby-monitor"]);
    assert.deepEqual(opts, []);
    assert.deepEqual(panel.state().optedInScreenIds, []);
  });

  it("updates defensively, removes stale consent, and represents an empty office", () => {
    const { panel } = setup();
    const root = panel.root as unknown as FakeElement;
    root.find("data-action", "opt-in").dispatch("click");
    const next = [{ ...SURFACES[1]!, screenId: "safe-text-<script>" }];
    panel.update(next);
    next[0]!.screenId = "mutated";
    assert.equal(panel.state().selectedId, "safe-text-<script>");
    assert.deepEqual(panel.state().optedInScreenIds, []);
    assert.match(root.text(), /safe-text-<script>/);
    panel.update([]);
    assert.equal(panel.state().selectedId, null);
    assert.match(root.text(), /No authored office screens/);
  });

  it("shows concise remote lifecycle states and forgets them with their screen", () => {
    const { panel, stops, opts } = setup();
    const root = panel.root as unknown as FakeElement;
    root.find("data-action", "opt-in").dispatch("click");
    panel.setRemoteStatus("lobby-monitor", "connecting");
    assert.equal(panel.state().remoteStatusByScreen["lobby-monitor"], "connecting");
    assert.match(root.text(), /Remote connecting/);
    assert.equal(root.find("data-action", "stop").disabled, false);
    root.find("data-action", "stop").dispatch("click");
    assert.deepEqual(stops, ["lobby-monitor"]);
    panel.setRemoteStatus("lobby-monitor", "unavailable");
    assert.match(root.text(), /No remote share/);
    assert.equal(root.find("data-action", "opt-in").textContent, "Retry remote");
    root.find("data-action", "opt-in").dispatch("click");
    assert.deepEqual(opts.at(-1), ["lobby-monitor", true]);
    assert.deepEqual(panel.state().optedInScreenIds, ["lobby-monitor"]);
    panel.update([SURFACES[1]!]);
    assert.deepEqual(panel.state().remoteStatusByScreen, {});
    assert.throws(() => panel.setRemoteStatus("commons-display", "invalid" as "live"), /invalid remote status/);
  });

  it("refreshes a remote surface from bound back to cleared", () => {
    const { panel } = setup();
    const root = panel.root as unknown as FakeElement;
    panel.setRemoteStatus("lobby-monitor", "live");
    panel.update([{ ...SURFACES[0]!, bound: true }, SURFACES[1]!]);
    assert.match(root.text(), /Media active · Remote live/);
    panel.update(SURFACES);
    panel.setRemoteStatus("lobby-monitor", "off");
    assert.match(root.text(), /Media off · Remote off/);
    assert.equal(root.find("data-action", "stop").disabled, true);
  });

  it("traps tab focus, closes on Escape, restores focus, and disposes idempotently", () => {
    const { document, container, panel } = setup();
    const trigger = document.createElement("button");
    trigger.focus();
    panel.open();
    const root = panel.root as unknown as FakeElement;
    const first = root.find("data-screen-id", "lobby-monitor");
    const last = root.find("data-action", "close");
    last.focus();
    assert.equal(root.dispatch("keydown", { key: "Tab" }).defaultPrevented, true);
    assert.equal(document.activeElement, first);
    first.focus();
    assert.equal(root.dispatch("keydown", { key: "Tab", shiftKey: true }).defaultPrevented, true);
    assert.equal(document.activeElement, last);
    assert.equal(root.dispatch("keydown", { key: "Escape" }).defaultPrevented, true);
    assert.equal(panel.state().open, false);
    assert.equal(document.activeElement, trigger);
    panel.dispose();
    panel.dispose();
    assert.equal(container.children.length, 0);
  });
});
