/** Accessible, capture-free command panel for authored office screens. */

import type { MediaSurfaceDescriptor } from "./presentation.ts";

export type OfficeScreenRemoteStatus = "off" | "connecting" | "live" | "reconnecting" | "stopped" | "unavailable" | "error";

export interface OfficeScreenPanelOptions {
  container: HTMLElement;
  surfaces: readonly MediaSurfaceDescriptor[];
  onSelect?: (surface: MediaSurfaceDescriptor) => void;
  /** Intent only. The caller may prompt for capture after receiving it. */
  onRequestShare?: (surface: MediaSurfaceDescriptor) => void;
  /** Intent only. The caller owns presenter stop/revoke policy and media. */
  onStopShare?: (surface: MediaSurfaceDescriptor) => void;
  onViewerOptIn?: (surface: MediaSurfaceDescriptor, optedIn: boolean) => void;
}

export interface OfficeScreenPanelState {
  open: boolean;
  selectedId: string | null;
  /** Explicit, local viewing choices. Empty initially and after disposal. */
  optedInScreenIds: string[];
  surfaces: MediaSurfaceDescriptor[];
  remoteStatusByScreen: Record<string, OfficeScreenRemoteStatus>;
}

export interface OfficeScreenPanel {
  root: HTMLElement;
  open(): OfficeScreenPanelState;
  update(surfaces: readonly MediaSurfaceDescriptor[]): OfficeScreenPanelState;
  close(): OfficeScreenPanelState;
  state(): OfficeScreenPanelState;
  setRemoteStatus(screenId: string, status: OfficeScreenRemoteStatus): OfficeScreenPanelState;
  dispose(): void;
}

const STYLES = `
.tera-screen-panel { width:min(520px,calc(100vw - 32px)); max-height:min(720px,calc(100dvh - 32px)); overflow:auto; padding:20px; color:#e8edf2; background:rgba(9,13,18,.94); border:1px solid rgba(255,255,255,.14); border-radius:8px; font:12px/1.5 ui-monospace,monospace; }
.tera-screen-panel__title { margin:0 0 4px; font-size:17px; }
.tera-screen-panel__intro,.tera-screen-panel__empty { color:rgba(255,255,255,.65); }
.tera-screen-panel__list { display:grid; gap:8px; margin:16px 0; }
.tera-screen-panel__screen { width:100%; padding:10px; color:inherit; text-align:left; background:rgba(255,255,255,.05); border:1px solid rgba(255,255,255,.13); border-radius:5px; cursor:pointer; }
.tera-screen-panel__screen[aria-pressed="true"] { border-color:#f2b134; background:rgba(242,177,52,.1); }
.tera-screen-panel__meta { display:block; color:rgba(255,255,255,.58); }
.tera-screen-panel__actions { display:flex; flex-wrap:wrap; justify-content:flex-end; gap:8px; }
.tera-screen-panel__button { min-height:40px; padding:8px 12px; color:inherit; background:rgba(255,255,255,.07); border:1px solid rgba(255,255,255,.15); border-radius:5px; cursor:pointer; }
.tera-screen-panel__button:disabled { opacity:.4; cursor:not-allowed; }
.tera-screen-panel :focus-visible { outline:2px solid #f2b134; outline-offset:2px; }
`;

let panelSequence = 0;

export function createOfficeScreenPanel(options: OfficeScreenPanelOptions): OfficeScreenPanel {
  if (!options.container || typeof options.container.append !== "function") {
    throw new RangeError("office screen panel: container must be an HTMLElement");
  }
  let surfaces = validateSurfaces(options.surfaces);
  let selectedId: string | null = surfaces[0]?.screenId ?? null;
  const optedIn = new Set<string>();
  const remoteStatuses = new Map<string, OfficeScreenRemoteStatus>();
  let isOpen = false;
  let disposed = false;
  let invoker: HTMLElement | null = null;

  const doc = options.container.ownerDocument;
  const number = ++panelSequence;
  const titleId = `tera-screen-panel-title-${number}`;
  const introId = `tera-screen-panel-intro-${number}`;
  const root = doc.createElement("section");
  root.className = "tera-screen-panel";
  root.setAttribute("role", "dialog");
  root.setAttribute("aria-modal", "true");
  root.setAttribute("aria-labelledby", titleId);
  root.setAttribute("aria-describedby", introId);
  root.setAttribute("aria-hidden", "true");
  root.hidden = true;

  const style = doc.createElement("style");
  style.textContent = STYLES;
  const title = doc.createElement("h2");
  title.id = titleId;
  title.className = "tera-screen-panel__title";
  title.textContent = "Office screens";
  const intro = doc.createElement("p");
  intro.id = introId;
  intro.className = "tera-screen-panel__intro";
  intro.textContent = "Media is off by default. Opt in to view a remote screen, or choose Share screen to present your own.";
  const list = doc.createElement("div");
  list.className = "tera-screen-panel__list";
  list.setAttribute("role", "list");
  const actions = doc.createElement("div");
  actions.className = "tera-screen-panel__actions";
  const optButton = button(doc, "Opt in to view", "opt-in");
  const shareButton = button(doc, "Share screen", "share");
  const stopButton = button(doc, "Stop / revoke", "stop");
  const closeButton = button(doc, "Close", "close");
  actions.append(optButton, shareButton, stopButton, closeButton);
  root.append(style, title, intro, list, actions);
  options.container.append(root);

  function selected(): MediaSurfaceDescriptor | null {
    return surfaces.find((surface) => surface.screenId === selectedId) ?? null;
  }

  function remoteCanStop(screenId: string): boolean {
    const status = remoteStatuses.get(screenId);
    return status === "connecting" || status === "live" || status === "reconnecting";
  }

  function render(): void {
    for (const child of [...list.children]) child.remove();
    if (surfaces.length === 0) {
      const empty = doc.createElement("p");
      empty.className = "tera-screen-panel__empty";
      empty.textContent = "No authored office screens are available.";
      list.append(empty);
    }
    for (const surface of surfaces) {
      const row = button(doc, "", "select");
      row.className = "tera-screen-panel__screen";
      row.setAttribute("role", "listitem");
      row.setAttribute("data-screen-id", surface.screenId);
      row.setAttribute("aria-pressed", String(surface.screenId === selectedId));
      const name = doc.createElement("span");
      name.textContent = surface.screenId;
      const meta = doc.createElement("span");
      meta.className = "tera-screen-panel__meta";
      const room = surface.roomId ?? "Unassigned room";
      const status = surface.bound ? "Media active" : "Media off";
      const remote = remoteStatuses.get(surface.screenId) ?? "off";
      const remoteLabel = remote === "off" ? "Remote off"
        : remote === "connecting" ? "Remote connecting"
          : remote === "live" ? "Remote live"
            : remote === "reconnecting" ? "Remote reconnecting"
              : remote === "stopped" ? "Remote stopped"
                : remote === "unavailable" ? "No remote share"
                  : "Remote error";
      meta.textContent = `${surface.levelId} · ${room} · ${status} · ${remoteLabel}`;
      row.append(name, meta);
      row.addEventListener("click", () => {
        selectedId = surface.screenId;
        render();
        options.onSelect?.(copySurface(surface));
      });
      list.append(row);
    }
    const surface = selected();
    const viewing = surface ? optedIn.has(surface.screenId) : false;
    optButton.disabled = surface === null;
    // Presenting is its own explicit action. Requiring viewer opt-in here would
    // start a remote viewer join immediately before replacing it with capture.
    shareButton.disabled = surface === null;
    stopButton.disabled = surface === null || (!surface.bound && !remoteCanStop(surface.screenId));
    const retry = surface !== null && viewing && ["stopped", "unavailable", "error"]
      .includes(remoteStatuses.get(surface.screenId) ?? "off");
    optButton.textContent = retry ? "Retry remote" : viewing ? "Stop viewing" : "Opt in to view";
    optButton.setAttribute("aria-pressed", String(viewing));
  }

  optButton.addEventListener("click", () => {
    const surface = selected();
    if (!surface) return;
    const remote = remoteStatuses.get(surface.screenId);
    if (optedIn.has(surface.screenId) &&
        (remote === "stopped" || remote === "unavailable" || remote === "error")) {
      render();
      options.onViewerOptIn?.(copySurface(surface), true);
      return;
    }
    const next = !optedIn.has(surface.screenId);
    if (next) optedIn.add(surface.screenId);
    else optedIn.delete(surface.screenId);
    render();
    options.onViewerOptIn?.(copySurface(surface), next);
  });
  shareButton.addEventListener("click", () => {
    const surface = selected();
    if (surface) options.onRequestShare?.(copySurface(surface));
  });
  stopButton.addEventListener("click", () => {
    const surface = selected();
    if (surface && (surface.bound || remoteCanStop(surface.screenId))) {
      options.onStopShare?.(copySurface(surface));
    }
  });
  closeButton.addEventListener("click", () => close());

  function snapshot(): OfficeScreenPanelState {
    return {
      open: isOpen,
      selectedId,
      optedInScreenIds: [...optedIn],
      surfaces: surfaces.map(copySurface),
      remoteStatusByScreen: Object.fromEntries(remoteStatuses),
    };
  }

  function close(): OfficeScreenPanelState {
    if (disposed || !isOpen) return snapshot();
    isOpen = false;
    root.hidden = true;
    root.setAttribute("aria-hidden", "true");
    invoker?.focus();
    invoker = null;
    return snapshot();
  }

  root.addEventListener("keydown", (event) => {
    if (event.key === "Escape") {
      event.preventDefault();
      close();
      return;
    }
    if (event.key !== "Tab") return;
    const focusable = controls();
    if (focusable.length === 0) return;
    const first = focusable[0];
    const last = focusable.at(-1);
    if (!first || !last) return;
    if (!event.shiftKey && doc.activeElement === last) {
      event.preventDefault();
      first.focus();
    } else if (event.shiftKey && doc.activeElement === first) {
      event.preventDefault();
      last.focus();
    }
  });

  function controls(): HTMLElement[] {
    const rows = [...list.children].filter((child): child is HTMLElement =>
      (child as HTMLElement).getAttribute("data-screen-id") !== null,
    );
    return [...rows, optButton, shareButton, stopButton, closeButton].filter(
      (control) => !(control as HTMLButtonElement).disabled,
    );
  }

  render();
  return {
    root,
    open() {
      if (disposed) return snapshot();
      const active = doc.activeElement as HTMLElement | null;
      invoker = active && typeof active.focus === "function" ? active : null;
      isOpen = true;
      root.hidden = false;
      root.setAttribute("aria-hidden", "false");
      controls()[0]?.focus();
      return snapshot();
    },
    update(next) {
      if (disposed) return snapshot();
      surfaces = validateSurfaces(next);
      const ids = new Set(surfaces.map((surface) => surface.screenId));
      for (const id of optedIn) if (!ids.has(id)) optedIn.delete(id);
      for (const id of remoteStatuses.keys()) if (!ids.has(id)) remoteStatuses.delete(id);
      if (selectedId === null || !ids.has(selectedId)) selectedId = surfaces[0]?.screenId ?? null;
      render();
      return snapshot();
    },
    close,
    state: snapshot,
    setRemoteStatus(screenId, status) {
      if (disposed || !surfaces.some((surface) => surface.screenId === screenId)) return snapshot();
      if (!["off", "connecting", "live", "reconnecting", "stopped", "unavailable", "error"].includes(status)) {
        throw new TypeError("office screen panel: invalid remote status");
      }
      if (status === "off") remoteStatuses.delete(screenId);
      else remoteStatuses.set(screenId, status);
      render();
      return snapshot();
    },
    dispose() {
      if (disposed) return;
      close();
      disposed = true;
      optedIn.clear();
      remoteStatuses.clear();
      surfaces = [];
      selectedId = null;
      root.remove();
    },
  };
}

function validateSurfaces(input: readonly MediaSurfaceDescriptor[]): MediaSurfaceDescriptor[] {
  if (!Array.isArray(input)) throw new TypeError("office screen panel: surfaces must be an array");
  const seen = new Set<string>();
  return input.map((surface) => {
    if (!surface || typeof surface !== "object") throw new TypeError("office screen panel: invalid surface");
    for (const field of ["screenId", "officeId", "levelId", "kind"] as const) {
      if (typeof surface[field] !== "string" || surface[field].length === 0) {
        throw new TypeError(`office screen panel: invalid ${field}`);
      }
    }
    if (surface.roomId !== null && typeof surface.roomId !== "string") {
      throw new TypeError("office screen panel: invalid roomId");
    }
    if (typeof surface.bound !== "boolean") throw new TypeError("office screen panel: invalid bound state");
    if (seen.has(surface.screenId)) throw new TypeError("office screen panel: duplicate screenId");
    seen.add(surface.screenId);
    return copySurface(surface);
  });
}

function copySurface(surface: MediaSurfaceDescriptor): MediaSurfaceDescriptor {
  return { ...surface };
}

function button(doc: Document, label: string, action: string): HTMLButtonElement {
  const value = doc.createElement("button");
  value.type = "button";
  value.className = "tera-screen-panel__button";
  value.textContent = label;
  value.setAttribute("data-action", action);
  return value;
}
