(() => {
  const stage = document.querySelector("[data-overlay-preview-window]");
  const previewViewport = document.querySelector("[data-preview-window-viewport]");
  const frame = document.querySelector("[data-preview-window-frame]");
  const canvas = document.querySelector("[data-preview-window-canvas]");
  const layer = document.querySelector("[data-preview-window-layer]");
  const status = document.querySelector("[data-preview-window-status]");
  const readout = document.querySelector("[data-popout-readout]");
  const sceneSelect = document.querySelector("[data-popout-scene]");
  const sourceSelect = document.querySelector("[data-popout-source]");
  const snapToggle = document.querySelector("[data-popout-snap]");
  const guideX = document.querySelector("[data-popout-guide-x]");
  const guideY = document.querySelector("[data-popout-guide-y]");
  const stateData = document.querySelector("[data-overlay-preview-state]");
  const renderer = window.LumiOverlayRenderer;
  if (!stage || !previewViewport || !frame || !canvas || !layer || !stateData || !renderer) return;

  const anchorFactors = {
    "top-left": [0, 0], "top-center": [0.5, 0], "top-right": [1, 0],
    "center-left": [0, 0.5], center: [0.5, 0.5], "center-right": [1, 0.5],
    "bottom-left": [0, 1], "bottom-center": [0.5, 1], "bottom-right": [1, 1]
  };
  let editorState = JSON.parse(stateData.textContent || "{}");
  let selectedId = null;
  let elements = new Map();
  let drag = null;
  let saveTimer = null;
  const channelName = `lumi-overlay-preview:${editorState.overlayId}`;
  let channel = null;

  const clamp = (number, min, max) => Math.min(max, Math.max(min, number));
  const round = (number) => Math.round(number * 100) / 100;
  const selectedScene = () => editorState.scenes?.find((scene) => scene.id === sceneSelect.value)
    || editorState.scenes?.find((scene) => scene.id === editorState.previewSceneId)
    || editorState.scenes?.find((scene) => scene.id === editorState.activeSceneId)
    || editorState.scenes?.[0]
    || null;
  const selectedModule = () => selectedScene()?.modules.find((module) => module.id === selectedId) || null;

  function edges(config) {
    const [ax, ay] = anchorFactors[config.anchor] || anchorFactors["top-left"];
    const width = Number(config.width || 0);
    const height = Number(config.height || 0);
    const left = Number(config.x || 0) - ax * width;
    const top = Number(config.y || 0) - ay * height;
    return { left, top, right: left + width, bottom: top + height };
  }

  function configFromEdges(box, anchor) {
    const [ax, ay] = anchorFactors[anchor] || anchorFactors["top-left"];
    const width = clamp(box.right - box.left, 0.5, 100);
    const height = clamp(box.bottom - box.top, 0.5, 100);
    return {
      x: clamp(box.left + ax * width, 0, 100),
      y: clamp(box.top + ay * height, 0, 100),
      width,
      height
    };
  }

  function fitFrame() {
    const width = Math.max(1, Number(editorState.canvas?.width) || 1920);
    const height = Math.max(1, Number(editorState.canvas?.height) || 1080);
    const scale = Math.min(previewViewport.clientWidth / width, previewViewport.clientHeight / height);
    frame.style.width = `${Math.max(1, width * scale)}px`;
    frame.style.height = `${Math.max(1, height * scale)}px`;
    frame.style.setProperty("--overlay-aspect", `${width} / ${height}`);
  }

  function updateReadout() {
    const module = selectedModule();
    if (!module) {
      readout.textContent = "No source selected";
      return;
    }
    readout.textContent = `${module.name} · X ${round(module.config.x)}% · Y ${round(module.config.y)}% · ${round(module.config.width)} × ${round(module.config.height)}%`;
  }

  function selectSource(moduleId, { focus = true } = {}) {
    selectedId = moduleId;
    layer.querySelectorAll(".lumi-overlay-module").forEach((element) => element.classList.toggle("is-selected", element.dataset.moduleId === moduleId));
    sourceSelect.value = moduleId || "";
    updateReadout();
    if (focus) canvas.focus({ preventScroll: true });
  }

  function syncControls() {
    const sceneId = selectedScene()?.id || "";
    sceneSelect.replaceChildren(...(editorState.scenes || []).map((scene) => new Option(scene.name, scene.id)));
    sceneSelect.value = sceneId;
    const scene = selectedScene();
    sourceSelect.replaceChildren(new Option("Choose a source", ""), ...(scene?.modules || []).filter((module) => module.enabled).map((module) => new Option(module.name, module.id)));
    sourceSelect.disabled = !(scene?.modules || []).some((module) => module.enabled);
  }

  function render() {
    fitFrame();
    syncControls();
    const scene = selectedScene();
    elements = renderer.render(layer, { enabled: true, canvas: editorState.canvas, scene }, { editor: true, canvas: editorState.canvas });
    if (!elements.has(selectedId)) selectedId = elements.keys().next().value || null;
    selectSource(selectedId, { focus: false });
    if (!/^(Saving|Saved)/.test(status.textContent)) status.textContent = `${editorState.canvas?.width || 1920} × ${editorState.canvas?.height || 1080} · drag, resize, or use arrow keys`;
  }

  function resize() {
    fitFrame();
    const renderedStage = layer.querySelector(":scope > .lumi-overlay-stage");
    if (!renderedStage) return;
    const layout = renderer.stageLayout(layer, editorState.canvas);
    renderedStage.style.width = `${layout.width}px`;
    renderedStage.style.height = `${layout.height}px`;
    renderedStage.style.transform = `scale(${layout.scaleX}, ${layout.scaleY})`;
  }

  function showGuide(axis, value) {
    const guide = axis === "x" ? guideX : guideY;
    guide.style[axis === "x" ? "left" : "top"] = `${value}%`;
    guide.classList.add("is-visible");
  }

  function clearGuides() {
    guideX.classList.remove("is-visible");
    guideY.classList.remove("is-visible");
  }

  function snapAdjustment(points, threshold) {
    let best = null;
    for (const point of points) for (const target of [0, 50, 100]) {
      const difference = target - point;
      if (Math.abs(difference) <= threshold && (!best || Math.abs(difference) < Math.abs(best.difference))) best = { difference, target };
    }
    return best;
  }

  function snappingActive(event) {
    return Boolean(snapToggle.checked) !== Boolean(event.ctrlKey || event.metaKey);
  }

  function applyDrag(event) {
    if (!drag) return;
    const module = selectedModule();
    if (!module) return;
    const dx = ((event.clientX - drag.startX) / drag.rect.width) * 100;
    const dy = ((event.clientY - drag.startY) / drag.rect.height) * 100;
    const start = edges(drag.config);
    const box = { ...start };
    if (drag.handle) {
      if (drag.handle.includes("w")) box.left += dx;
      if (drag.handle.includes("e")) box.right += dx;
      if (drag.handle.includes("n")) box.top += dy;
      if (drag.handle.includes("s")) box.bottom += dy;
      if (box.right - box.left < 0.5) box.right = box.left + 0.5;
      if (box.bottom - box.top < 0.5) box.bottom = box.top + 0.5;
    } else {
      box.left += dx; box.right += dx; box.top += dy; box.bottom += dy;
    }
    clearGuides();
    if (snappingActive(event)) {
      const xPoints = drag.handle ? [drag.handle.includes("w") ? box.left : drag.handle.includes("e") ? box.right : (box.left + box.right) / 2] : [box.left, (box.left + box.right) / 2, box.right];
      const yPoints = drag.handle ? [drag.handle.includes("n") ? box.top : drag.handle.includes("s") ? box.bottom : (box.top + box.bottom) / 2] : [box.top, (box.top + box.bottom) / 2, box.bottom];
      const snapX = snapAdjustment(xPoints, (7 / drag.rect.width) * 100);
      const snapY = snapAdjustment(yPoints, (7 / drag.rect.height) * 100);
      if (snapX) {
        if (drag.handle?.includes("w")) box.left += snapX.difference;
        else if (drag.handle?.includes("e")) box.right += snapX.difference;
        else { box.left += snapX.difference; box.right += snapX.difference; }
        showGuide("x", snapX.target);
      }
      if (snapY) {
        if (drag.handle?.includes("n")) box.top += snapY.difference;
        else if (drag.handle?.includes("s")) box.bottom += snapY.difference;
        else { box.top += snapY.difference; box.bottom += snapY.difference; }
        showGuide("y", snapY.target);
      }
    }
    Object.assign(module.config, configFromEdges(box, module.config.anchor || "top-left"));
    renderer.applyBox(elements.get(module.id), module.config);
    updateReadout();
  }

  async function saveTransform(module) {
    if (!module) return;
    status.textContent = "Saving adjustment…";
    try {
      const response = await fetch(`/api/admin/overlays/${editorState.overlayId}/modules/${module.id}/transform`, {
        method: "POST",
        credentials: "same-origin",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          x: module.config.x,
          y: module.config.y,
          width: module.config.width,
          height: module.config.height,
          anchor: module.config.anchor || "top-left"
        })
      });
      const payload = await response.json().catch(() => ({}));
      if (!response.ok) throw new Error(payload.error || "The adjustment could not be saved.");
      Object.assign(module.config, payload.module.config);
      renderer.applyBox(elements.get(module.id), module.config);
      updateReadout();
      channel?.postMessage({ type: "transform", sceneId: selectedScene()?.id, moduleId: module.id, config: module.config });
      status.textContent = "Saved · editor synchronized";
    } catch (error) {
      status.textContent = `Save failed · ${error.message}`;
    }
  }

  function transformSelected(changes) {
    const module = selectedModule();
    if (!module) return;
    Object.assign(module.config, changes);
    renderer.applyBox(elements.get(module.id), module.config);
    updateReadout();
    saveTransform(module);
  }

  layer.addEventListener("pointerdown", (event) => {
    const element = event.target.closest(".lumi-overlay-module");
    if (!element) return;
    event.preventDefault();
    selectSource(element.dataset.moduleId);
    drag = {
      startX: event.clientX,
      startY: event.clientY,
      rect: canvas.getBoundingClientRect(),
      config: { ...selectedModule().config },
      handle: event.target.closest("[data-resize-handle]")?.dataset.resizeHandle || null
    };
  });
  window.addEventListener("pointermove", applyDrag);
  window.addEventListener("pointerup", () => {
    if (!drag) return;
    drag = null;
    clearGuides();
    saveTransform(selectedModule());
  });

  canvas.addEventListener("keydown", (event) => {
    if (!selectedModule() || !["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].includes(event.key)) return;
    event.preventDefault();
    const amount = event.shiftKey ? 1 : 0.1;
    const module = selectedModule();
    if (event.key === "ArrowLeft") module.config.x = clamp(Number(module.config.x) - amount, 0, 100);
    if (event.key === "ArrowRight") module.config.x = clamp(Number(module.config.x) + amount, 0, 100);
    if (event.key === "ArrowUp") module.config.y = clamp(Number(module.config.y) - amount, 0, 100);
    if (event.key === "ArrowDown") module.config.y = clamp(Number(module.config.y) + amount, 0, 100);
    renderer.applyBox(elements.get(module.id), module.config);
    updateReadout();
    clearTimeout(saveTimer);
    saveTimer = setTimeout(() => saveTransform(module), 280);
  });

  sceneSelect.addEventListener("change", () => { selectedId = null; editorState.previewSceneId = sceneSelect.value; render(); });
  sourceSelect.addEventListener("change", () => selectSource(sourceSelect.value));
  document.querySelector("[data-popout-center]")?.addEventListener("click", () => transformSelected({ x: 50, y: 50, anchor: "center" }));
  document.querySelector("[data-popout-fit]")?.addEventListener("click", () => transformSelected({ x: 0, y: 0, width: 100, height: 100, anchor: "top-left" }));
  document.querySelector("[data-popout-reset]")?.addEventListener("click", () => transformSelected({ x: 0, y: 0, width: 50, height: 25, anchor: "top-left" }));

  if ("BroadcastChannel" in window) {
    channel = new BroadcastChannel(channelName);
    channel.addEventListener("message", (event) => {
      if (event.data?.type !== "state" || !event.data.state) return;
      const currentSceneId = sceneSelect.value;
      editorState = event.data.state;
      editorState.previewSceneId = editorState.scenes?.some((scene) => scene.id === currentSceneId) ? currentSceneId : editorState.previewSceneId;
      render();
    });
    channel.postMessage({ type: "request-state" });
  } else {
    status.textContent = "Interactive preview opened · editor synchronization is unavailable in this browser";
  }

  window.addEventListener("resize", resize);
  window.addEventListener("beforeunload", () => {
    clearTimeout(saveTimer);
    channel?.close();
  }, { once: true });
  render();
})();
