(() => {
  "use strict";

  const boot = window.UI_BUILDER_BOOT || {};
  if (boot.mode !== "runtime") return;

  const selectedDeviceStorage = "hockey.vmix.selected_device";
  const state = {
    devices: [],
    activeDeviceId: "",
    activeDeviceIds: [],
    selectedDeviceId: localStorage.getItem(selectedDeviceStorage) || "",
    root: null,
    panelOpen: false,
    loading: false,
    timer: null,
    fastRefreshTimers: [],
    message: "",
    messageError: false,
    testOpenDeviceId: "",
    testCommand: { input: "", selectedName: "", value: "" },
  };

  const esc = (value) => String(value ?? "")
    .replaceAll("&", "&amp;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll('"', "&quot;")
    .replaceAll("'", "&#039;");

  async function api(path, options = {}) {
    const response = await fetch(path, {
      cache: "no-store",
      credentials: "same-origin",
      ...options,
      headers: {
        ...(options.body ? { "Content-Type": "application/json" } : {}),
        ...(options.headers || {}),
      },
    });
    let payload = {};
    try { payload = await response.json(); } catch (_) {}
    if (!response.ok) {
      const detail = typeof payload.detail === "string"
        ? payload.detail
        : payload.detail?.message || `HTTP ${response.status}`;
      throw new Error(detail);
    }
    return payload;
  }

  function runtimeData() {
    return window.UIBuilderRuntime?.getData?.() || {};
  }

  function currentGameId() {
    const data = runtimeData();
    return String(data.hockey?.selected_game?.external_id || data.hockey?.selected_game?.id || "").trim();
  }

  function currentSessionToken() {
    return String(runtimeData().hockey?.operator_session?.token || "").trim();
  }

  function activeDevices() {
    return state.devices.filter((item) => item.paired_to_me && item.active_for_account);
  }

  function selectedDevice() {
    return state.devices.find((item) => (
      item.device_id === state.selectedDeviceId
      && item.paired_to_me
      && item.active_for_account
    )) || null;
  }

  function selectLocalDevice(deviceId) {
    state.selectedDeviceId = String(deviceId || "").trim();
    if (state.selectedDeviceId) localStorage.setItem(selectedDeviceStorage, state.selectedDeviceId);
    else localStorage.removeItem(selectedDeviceStorage);
    window.dispatchEvent(new CustomEvent("hockey:agent-selected", {
      detail: { device_id: state.selectedDeviceId },
    }));
  }

  function mergeDevicePayload(payload) {
    if (!payload || !payload.device_id) return;
    const index = state.devices.findIndex((item) => item.device_id === payload.device_id);
    const previous = index >= 0 ? state.devices[index] : {};
    const merged = { ...previous, ...payload };
    if (merged.paired_to_me) merged.pair_state = "mine";
    else if (merged.paired) merged.pair_state = "busy";
    else merged.pair_state = "free";
    if (index >= 0) state.devices.splice(index, 1, merged);
    else state.devices.unshift(merged);
  }

  function scheduleFastRefresh() {
    state.fastRefreshTimers.forEach((timerId) => clearTimeout(timerId));
    state.fastRefreshTimers = [0, 250, 1000].map((delay) => setTimeout(() => {
      load(true).catch(() => {});
    }, delay));
  }

  function refreshMappingInBackground(deviceId, assignment = null) {
    if (!deviceId || !assignment?.delivered) return;
    api(`/api/hockey/agents/devices/${encodeURIComponent(deviceId)}/apply-mapping`, { method: "POST" })
      .then(() => load(true))
      .catch((error) => console.warn("Could not refresh vMix Mapping after Agent selection", error));
  }

  async function bindSelectedDevice(deviceId) {
    selectLocalDevice(deviceId);
    const token = currentSessionToken();
    if (!token) return null;
    const result = await api(`/api/hockey/agents/devices/${encodeURIComponent(deviceId)}/select-session`, {
      method: "POST",
      // Agent selection must never wait for a full Mapping push. The session/match
      // assignment is committed first; Mapping is refreshed asynchronously below.
      body: JSON.stringify({ session_token: token, apply_mapping: false }),
    });
    refreshMappingInBackground(deviceId, result?.assignment || null);
    return result;
  }

  function setMessage(text, error = false) {
    state.message = text || "";
    state.messageError = error;
    render();
    if (text) setTimeout(() => {
      if (state.message === text) {
        state.message = "";
        render();
      }
    }, 3500);
  }

  function ensureRoot() {
    if (state.root?.isConnected) return state.root;
    const root = document.createElement("section");
    root.className = "hockey-agent-ui";
    root.innerHTML = `
      <button class="hockey-agent-chip" type="button" data-agent-toggle aria-expanded="false" aria-label="vMix Agent" title="vMix Agent">
        <span class="hockey-agent-dot"></span>
      </button>
      <div class="hockey-agent-panel" data-agent-panel hidden></div>
    `;
    const toolbar = document.querySelector(".runtime-topbar .runtime-actions");
    if (toolbar) toolbar.prepend(root);
    else document.body.appendChild(root);
    root.querySelector("[data-agent-toggle]")?.addEventListener("click", () => {
      state.panelOpen = !state.panelOpen;
      render();
    });
    root.addEventListener("click", onClick);
    root.addEventListener("input", (event) => {
      const field = event.target.closest("[data-agent-test-field]");
      if (!field) return;
      const key = field.dataset.agentTestField;
      if (key === "input") state.testCommand.input = field.value;
      if (key === "selectedName") state.testCommand.selectedName = field.value;
      if (key === "value") state.testCommand.value = field.value;
    });
    state.root = root;
    return root;
  }

  async function onClick(event) {
    const button = event.target.closest("button[data-agent-action]");
    if (!button || state.loading) return;
    const action = button.dataset.agentAction;
    const deviceId = button.dataset.deviceId || "";
    if (!deviceId) return;
    state.loading = true;
    render();
    try {
      if (action === "pair") {
        const paired = await api(`/api/hockey/agents/devices/${encodeURIComponent(deviceId)}/pair`, {
          method: "POST",
          body: JSON.stringify({ make_active: true }),
        });
        mergeDevicePayload(paired);
        render();
        await bindSelectedDevice(deviceId);
        scheduleFastRefresh();
        setMessage("Agent прикреплён и выбран для этой панели");
      } else if (action === "activate") {
        const activated = await api(`/api/hockey/agents/devices/${encodeURIComponent(deviceId)}/activate`, { method: "POST" });
        mergeDevicePayload(activated);
        render();
        await bindSelectedDevice(deviceId);
        scheduleFastRefresh();
        setMessage("Agent включён и выбран для этой панели");
      } else if (action === "select") {
        await bindSelectedDevice(deviceId);
        scheduleFastRefresh();
        setMessage("Эта панель теперь управляет выбранным Agent");
      } else if (action === "deactivate") {
        await api(`/api/hockey/agents/devices/${encodeURIComponent(deviceId)}/deactivate`, { method: "POST" });
        if (state.selectedDeviceId === deviceId) selectLocalDevice("");
        setMessage("Получение данных для Agent остановлено");
      } else if (action === "test-toggle") {
        state.testOpenDeviceId = state.testOpenDeviceId === deviceId ? "" : deviceId;
      } else if (action === "test-set-text") {
        const input = state.testCommand.input.trim();
        const selectedName = state.testCommand.selectedName.trim();
        if (!input || !selectedName) throw new Error("Заполните Input и имя текстового поля");
        const result = await api(`/api/hockey/agents/devices/${encodeURIComponent(deviceId)}/test-set-text`, {
          method: "POST",
          body: JSON.stringify({ input, selected_name: selectedName, value: state.testCommand.value }),
        });
        setMessage(`SetText выполнен · матч ${result.match_id}`);
      } else if (action === "unpair") {
        if (!window.confirm("Отвязать этот agent от вашего аккаунта?")) return;
        await api(`/api/hockey/agents/devices/${encodeURIComponent(deviceId)}/pair`, { method: "DELETE" });
        const existing = state.devices.find((item) => item.device_id === deviceId);
        if (existing) mergeDevicePayload({
          ...existing,
          paired: false, paired_to_me: false, active_for_account: false, pair_state: "free",
          current_match_id: "", assignment_id: "", owner: "",
        });
        if (state.testOpenDeviceId === deviceId) state.testOpenDeviceId = "";
        if (state.selectedDeviceId === deviceId) selectLocalDevice("");
        scheduleFastRefresh();
        setMessage("Agent отвязан");
      }
      await load(true);
    } catch (error) {
      setMessage(error.message || "Ошибка Agent", true);
    } finally {
      state.loading = false;
      render();
    }
  }

  async function load(force = false) {
    if (state.loading && !force) return;
    try {
      const payload = await api("/api/hockey/agents/devices");
      state.devices = Array.isArray(payload.devices) ? payload.devices : [];
      state.activeDeviceId = String(payload.active_device_id || "");
      state.activeDeviceIds = Array.isArray(payload.active_device_ids)
        ? payload.active_device_ids.map(String)
        : (state.activeDeviceId ? [state.activeDeviceId] : []);

      const selectedStillValid = state.devices.some((item) => (
        item.device_id === state.selectedDeviceId
        && item.paired_to_me
        && item.active_for_account
      ));
      if (!selectedStillValid) {
        const mine = activeDevices();
        if (mine.length === 1) selectLocalDevice(mine[0].device_id);
        else if (state.selectedDeviceId) selectLocalDevice("");
      }

      const editingTest = Boolean(state.root?.querySelector("[data-agent-test-form]")?.contains(document.activeElement));
      if (!editingTest) render();
    } catch (error) {
      if (!String(error.message || "").includes("401")) console.warn("Could not load vMix agents", error);
    }
  }

  function deviceCard(item) {
    const online = Boolean(item.online);
    const vmix = Boolean(item.vmix_connected);
    const mine = Boolean(item.paired_to_me);
    const active = Boolean(item.active_for_account);
    const selectedHere = state.selectedDeviceId === item.device_id;
    const free = item.pair_state === "free";
    const busy = item.pair_state === "busy";
    const currentMatch = String(item.current_match_id || "");

    let actions = "";
    if (free && online) {
      actions = `<button type="button" data-agent-action="pair" data-device-id="${esc(item.device_id)}">Прикрепить</button>`;
    } else if (mine) {
      const canTest = active && selectedHere && online && vmix && currentMatch;
      actions = `
        ${active
          ? '<span class="hockey-agent-badge active">Получает данные</span>'
          : `<button type="button" data-agent-action="activate" data-device-id="${esc(item.device_id)}">Включить</button>`}
        ${active
          ? (selectedHere
            ? '<span class="hockey-agent-badge active">Эта панель</span>'
            : `<button type="button" data-agent-action="select" data-device-id="${esc(item.device_id)}">Использовать здесь</button>`)
          : ""}
        ${canTest ? `<button class="secondary" type="button" data-agent-action="test-toggle" data-device-id="${esc(item.device_id)}">${state.testOpenDeviceId === item.device_id ? "Скрыть тест" : "Тест SetText"}</button>` : ""}
        ${active ? `<button class="secondary" type="button" data-agent-action="deactivate" data-device-id="${esc(item.device_id)}">Не получать</button>` : ""}
        <button class="danger" type="button" data-agent-action="unpair" data-device-id="${esc(item.device_id)}">Отвязать</button>
      `;
    } else if (busy) {
      actions = '<span class="hockey-agent-badge busy">Занято другим оператором</span>';
    }

    const testForm = state.testOpenDeviceId === item.device_id ? `
      <div class="hockey-agent-test" data-agent-test-form>
        <div class="hockey-agent-test-title"><strong>Тест Web → Agent → vMix</strong><small>SetText</small></div>
        <label>Input <input data-agent-test-field="input" value="${esc(state.testCommand.input)}" placeholder="например Scorebug"></label>
        <label>Поле <input data-agent-test-field="selectedName" value="${esc(state.testCommand.selectedName)}" placeholder="например HomeTeam.Text"></label>
        <label>Текст <input data-agent-test-field="value" value="${esc(state.testCommand.value)}" placeholder="TEST FROM WEB"></label>
        <button type="button" data-agent-action="test-set-text" data-device-id="${esc(item.device_id)}">Отправить SetText</button>
        <small>Тест выполняется только на этом Agent и его текущем матче.</small>
      </div>` : "";

    return `
      <article class="hockey-agent-device ${online ? "is-online" : "is-offline"} ${active ? "is-active" : ""} ${selectedHere ? "is-selected-here" : ""}">
        <div class="hockey-agent-device-title">
          <span class="hockey-agent-status-light ${online ? "online" : "offline"}"></span>
          <div><strong>${esc(item.name || item.hostname || item.device_id)}</strong><code>${esc(item.device_id)}</code></div>
        </div>
        <div class="hockey-agent-device-grid">
          <span>Agent <b>${online ? "ONLINE" : "OFFLINE"}</b></span>
          <span>vMix <b class="${vmix ? "ok" : "warn"}">${vmix ? "CONNECTED" : "NO CONNECTION"}</b></span>
          ${currentMatch ? `<span>Матч <b>${esc(currentMatch)}</b></span>` : ""}
          ${item.mapping ? `<span>Mapping <b class="ok">${esc(item.mapping.name)} · v${esc(item.mapping.version)}</b></span>` : (item.project_fingerprint ? `<span>Mapping <b class="warn">НЕ НАСТРОЕН</b></span>` : "")}
          ${item.project_input_count ? `<span>vMix структура <b>${esc(item.project_input_count)} Inputs · ${esc(item.project_field_count || 0)} полей</b></span>` : ""}
          ${item.agent_version ? `<span>Версия <b>${esc(item.agent_version)}</b></span>` : ""}
        </div>
        <div class="hockey-agent-actions">${actions}</div>
        ${testForm}
      </article>
    `;
  }

  function render() {
    const root = ensureRoot();
    const chip = root.querySelector("[data-agent-toggle]");
    const panel = root.querySelector("[data-agent-panel]");
    if (!chip || !panel) return;

    const active = activeDevices();
    const selected = selectedDevice();
    const freeOnline = state.devices.filter((item) => item.online && item.pair_state === "free").length;
    root.classList.toggle("has-active", Boolean(selected?.online));
    root.classList.toggle("has-vmix", Boolean(selected?.online && selected?.vmix_connected));
    root.classList.toggle("is-offline", Boolean(selected && !selected.online));

    let chipTitle = "vMix Agent · не подключён";
    if (selected) {
      const name = selected.name || selected.hostname || "Agent";
      const vmixState = selected.vmix_connected ? "vMix подключён" : "vMix не подключён";
      chipTitle = `${name} · ${vmixState}${selected.current_match_id ? ` · матч ${selected.current_match_id}` : ""}`;
    } else if (active.length > 1) {
      chipTitle = `vMix Agent · ${active.length} активных · выберите устройство`;
    } else if (freeOnline) {
      chipTitle = `vMix Agent · ${freeOnline} свободных устройств`;
    }
    chip.title = chipTitle;
    chip.setAttribute("aria-label", chipTitle);
    chip.setAttribute("aria-expanded", state.panelOpen ? "true" : "false");
    panel.hidden = !state.panelOpen;

    if (!state.panelOpen) return;
    const gameId = currentGameId();
    const cards = state.devices.length
      ? state.devices.map(deviceCard).join("")
      : `<div class="hockey-agent-empty">Активных agent.exe пока нет.<br>Запустите agent.exe на компьютере с vMix.</div>`;
    panel.innerHTML = `
      <header>
        <div><small>VMIX BRIDGE</small><strong>Устройства</strong></div>
        <button type="button" class="icon" data-agent-close aria-label="Закрыть">×</button>
      </header>
      <div class="hockey-agent-current-match">Web матч: <b>${esc(gameId || "не выбран")}</b></div>
      ${state.message ? `<div class="hockey-agent-notice ${state.messageError ? "error" : ""}">${esc(state.message)}</div>` : ""}
      <div class="hockey-agent-list">${cards}</div>
      <footer>Активных Agent может быть несколько. F1, Space, таймеры и другие команды идут только в Agent с меткой «Эта панель».</footer>
    `;
    panel.querySelector("[data-agent-close]")?.addEventListener("click", () => {
      state.panelOpen = false;
      render();
    });
  }

  window.HockeyAgentRuntime = {
    currentDeviceId: () => String(state.selectedDeviceId || ""),
    selectDevice: (deviceId) => { selectLocalDevice(deviceId); render(); },
    getDevices: () => state.devices.slice(),
  };

  window.addEventListener("hockey:game-selected", () => {
    setTimeout(() => load(true), 300);
    setTimeout(() => load(true), 1300);
  });
  window.addEventListener("focus", () => load(true));
  document.addEventListener("visibilitychange", () => {
    if (!document.hidden) load(true);
  });

  ensureRoot();
  load(true);
  state.timer = window.setInterval(() => {
    if (!document.hidden) load();
  }, 1500);
})();
