/** Accessible webcam-face controls. This view never acquires or retains media. */

export type WebcamFacePanelStatus = "off" | "requesting" | "active" | "error" | "unsupported";

export interface WebcamFacePanelState {
  status: WebcamFacePanelStatus;
  message: string;
  indicatorVisible: boolean;
}

export interface WebcamFacePanelOptions {
  /** Contextual controls, normally mounted inside the character editor. */
  container: HTMLElement;
  /** Persistent chrome that remains visible after the character editor closes. */
  indicatorContainer: HTMLElement;
  supported?: boolean;
  onStart: () => void | Promise<void>;
  onStop: () => void;
}

export interface WebcamFacePanel {
  root: HTMLElement;
  indicator: HTMLElement;
  /** Buttons to include in a containing modal's focus trap. */
  focusables: readonly HTMLElement[];
  state(): WebcamFacePanelState;
  update(status: WebcamFacePanelStatus, message?: string): WebcamFacePanelState;
  /** Restore modal keyboard focus after a browser permission prompt closes. */
  focusStop(): void;
  dispose(): void;
}

const STYLES = `
.tera-webcam-face { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--hairline, rgba(255,255,255,.13)); }
.tera-webcam-face__heading { margin: 0 0 4px; color: white; font: 600 13px/1.3 inherit; }
.tera-webcam-face__copy { margin: 0 0 10px; color: var(--ink-2, rgba(255,255,255,.62)); }
.tera-webcam-face__row { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
.tera-webcam-face__button {
  min-height: 40px; padding: 8px 14px; color: var(--ink, white); cursor: pointer;
  background: rgba(255,255,255,.07); border: 1px solid var(--hairline, rgba(255,255,255,.15));
  border-radius: var(--r-sm, 5px); font: inherit;
}
.tera-webcam-face__button:hover:not(:disabled) { background: rgba(255,255,255,.12); }
.tera-webcam-face__button:focus-visible { outline: 2px solid var(--amber, #f2b134); outline-offset: 2px; }
.tera-webcam-face__button:disabled { cursor: wait; opacity: .55; }
.tera-webcam-face__status { margin: 0; color: var(--ink-2, rgba(255,255,255,.62)); }
.tera-webcam-face[data-status="active"] .tera-webcam-face__status { color: #8fd7aa; }
.tera-webcam-face[data-status="error"] .tera-webcam-face__status,
.tera-webcam-face[data-status="unsupported"] .tera-webcam-face__status { color: #ffb2aa; }
/*
 * The "camera active" indicator.
 *
 * It used to position itself: fixed, right-aligned, and a top offset of
 * var(--s4) plus the safe-area inset plus 4.55rem — a number hand-derived from
 * the heights of two cards in index.html that this file cannot see, plus a
 * second copy of the same arithmetic under a media query. Any padding change in
 * either of those cards silently moved this on top of one of them.
 *
 * It is now a plain child of the one top-right flex column, so it has no
 * position of its own and no opinion about what is above it. Its *stacking*
 * decision moved with it: index.html raises that whole column to the alert
 * layer while — and only while — this element is unhidden, because an indicator
 * saying a camera is on is a safety affordance and a dialog that can cover it
 * turns "your webcam is live" into a fact the interface knows and the person
 * does not. That is one rule, in the file that owns the stacking order, instead
 * of a raw stacking literal here that nothing else on the page knew about.
 */
.tera-webcam-indicator {
  display: flex; align-items: center; gap: 9px; padding: 8px 10px;
  color: #dff8e8; background: rgba(8, 32, 20, .92); border: 1px solid rgba(100, 220, 145, .45);
  border-radius: var(--r-sm, 5px); box-shadow: var(--shadow, 0 8px 28px rgba(0,0,0,.35));
  font: 11px/1.4 ui-monospace, "SF Mono", Menlo, monospace;
}
.tera-webcam-indicator__text { flex: 1; min-width: 0; }
.tera-webcam-indicator__dot { width: 7px; height: 7px; border-radius: 50%; background: #65dc91; box-shadow: 0 0 0 3px rgba(101,220,145,.14); }
.tera-webcam-indicator__stop { min-height: 30px; padding: 4px 8px; color: inherit; background: transparent; border: 1px solid rgba(143,215,170,.4); border-radius: 4px; font: inherit; cursor: pointer; }
.tera-webcam-indicator__stop:focus-visible { outline: 2px solid var(--amber, #f2b134); outline-offset: 2px; }
/* No phone override any more: the column it lives in carries the breakpoint,
   which is the whole point of it being in a column. */
`;

const DEFAULT_MESSAGES: Record<WebcamFacePanelStatus, string> = {
  off: "Camera is off. Nothing is captured or stored.",
  requesting: "Waiting for camera permission…",
  active: "Live on your humanoid face. Never recorded or uploaded.",
  error: "Camera could not start. Check browser permission and try again.",
  unsupported: "Camera faces are not supported by this browser.",
};

export function createWebcamFacePanel(options: WebcamFacePanelOptions): WebcamFacePanel {
  if (!options.container || typeof options.container.append !== "function" ||
      !options.indicatorContainer || typeof options.indicatorContainer.append !== "function") {
    throw new RangeError("webcam face panel: valid containers are required");
  }
  if (typeof options.onStart !== "function" || typeof options.onStop !== "function") {
    throw new RangeError("webcam face panel: start and stop handlers are required");
  }
  const doc = options.container.ownerDocument;
  const root = doc.createElement("section");
  root.className = "tera-webcam-face";
  const style = doc.createElement("style");
  style.setAttribute("data-tera-webcam-face-style", "");
  style.textContent = STYLES;
  root.append(style);

  const heading = doc.createElement("h3");
  heading.className = "tera-webcam-face__heading";
  heading.textContent = "Live camera face";
  const copy = doc.createElement("p");
  copy.className = "tera-webcam-face__copy";
  copy.textContent = "Optionally put your camera on this humanoid for this tab only. It is never saved, recorded, or uploaded.";
  const row = doc.createElement("div");
  row.className = "tera-webcam-face__row";
  const start = doc.createElement("button");
  start.type = "button";
  start.className = "tera-webcam-face__button";
  start.setAttribute("data-action", "start-camera");
  start.textContent = "Start camera";
  const stop = doc.createElement("button");
  stop.type = "button";
  stop.className = "tera-webcam-face__button";
  stop.setAttribute("data-action", "stop-camera");
  stop.textContent = "Stop camera";
  const status = doc.createElement("p");
  status.className = "tera-webcam-face__status";
  status.setAttribute("role", "status");
  status.setAttribute("aria-live", "polite");
  row.append(start, stop, status);
  root.append(heading, copy, row);

  const indicator = doc.createElement("div");
  indicator.className = "tera-webcam-indicator";
  indicator.setAttribute("role", "region");
  indicator.setAttribute("aria-label", "Camera active");
  const dot = doc.createElement("span");
  dot.className = "tera-webcam-indicator__dot";
  dot.setAttribute("aria-hidden", "true");
  const indicatorText = doc.createElement("span");
  indicatorText.className = "tera-webcam-indicator__text";
  indicatorText.setAttribute("role", "status");
  indicatorText.setAttribute("aria-live", "polite");
  indicatorText.textContent = "Camera active · local face only";
  const indicatorStop = doc.createElement("button");
  indicatorStop.type = "button";
  indicatorStop.className = "tera-webcam-indicator__stop";
  indicatorStop.setAttribute("data-action", "stop-camera-indicator");
  indicatorStop.textContent = "Stop";
  indicator.append(dot, indicatorText, indicatorStop);

  options.container.append(root);
  options.indicatorContainer.append(indicator);
  let disposed = false;
  let current: WebcamFacePanelState = {
    status: options.supported === false ? "unsupported" : "off",
    message: DEFAULT_MESSAGES[options.supported === false ? "unsupported" : "off"],
    indicatorVisible: false,
  };

  function snapshot(): WebcamFacePanelState { return { ...current }; }
  function render(): void {
    root.setAttribute("data-status", current.status);
    status.textContent = current.message;
    const active = current.status === "active";
    const requesting = current.status === "requesting";
    start.hidden = active;
    start.disabled = requesting || current.status === "unsupported";
    stop.hidden = !active && !requesting;
    indicator.hidden = !current.indicatorVisible;
    // Starting capture completes asynchronously. Do not leave keyboard focus on
    // the Start button at the moment it becomes hidden; move it to the visible
    // in-dialog Stop control so Escape and the modal focus trap keep working.
    if (active) stop.focus();
  }
  function stopIntent(): void {
    if (!disposed && (current.status === "active" || current.status === "requesting")) options.onStop();
  }
  start.addEventListener("click", () => {
    if (!disposed && current.status !== "requesting" && current.status !== "active" && current.status !== "unsupported") {
      void options.onStart();
    }
  });
  stop.addEventListener("click", stopIntent);
  indicatorStop.addEventListener("click", stopIntent);
  render();

  return {
    root,
    indicator,
    focusables: [start, stop],
    state: snapshot,
    update(next, message = DEFAULT_MESSAGES[next]) {
      if (disposed) return snapshot();
      if (!(next in DEFAULT_MESSAGES) || typeof message !== "string") {
        throw new RangeError("webcam face panel: invalid state");
      }
      current = { status: next, message, indicatorVisible: next === "active" };
      render();
      return snapshot();
    },
    focusStop() {
      if (!disposed && current.status === "active") stop.focus();
    },
    dispose() {
      if (disposed) return;
      disposed = true;
      root.remove();
      indicator.remove();
    },
  };
}
