/**
 * ObserverTab — live entangled-state feed inside the extension panel.
 *
 * Polls the hash-linked transition feed every ~3 Zeqonds and renders it natively:
 *   • My machine  — GET /api/chain/:slug/explore (camelCase rows)
 *   • Network     — GET /api/chain/recent       (snake_case rows, all machines)
 * Watch any slug. Polling pauses automatically when the tab isn't visible.
 *
 * Depends on: window.ZeqConfig, window.ZeqAuth.
 *
 * @version 1.287.27
 * @license CC BY 4.0 — Zeq. H / Zeq 1.287 Hz Limited
 */
const ObserverTab = (() => {
  "use strict";

  const POLL_MS = 777; // 1 Zeqond — matches VS Code 1.287.35 cadence
  let _cid = null;
  let _mode = "mine"; // mine | network
  let _slug = "";
  let _timer = null;
  let _rows = [];
  let _oracles = [];
  let _status = "";

  function esc(s) { return String(s == null ? "" : s).replace(/[&<>"]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" }[c])); }
  const _seen = Object.create(null); // hash → 1; used to mint-flash brand-new rows
  async function _api() { return await ZeqConfig.api(); }
  function _authed() { return typeof ZeqAuth !== "undefined" && ZeqAuth.isAuthenticated(); }
  function _headers() { const h = {}; if (_authed()) Object.assign(h, ZeqAuth.getHeaders()); return h; }
  function _mySlug() { const u = ZeqAuth.getUser && ZeqAuth.getUser(); return u && u.zid ? String(u.zid).toLowerCase() : ""; }
  function _visible() { const t = document.getElementById("tab-observer"); return t && t.classList.contains("active"); }

  // Normalise a row from either feed shape into a common view model.
  function _norm(r) {
    return {
      type: r.transitionType || r.transition_type || r.type || "tick",
      hash: r.stateHash || r.state_hash || r.hash || "",
      zeqond: r.zeqondNumber || r.zeqond_number || r.zeqond || "",
      phase: typeof r.phase === "number" ? r.phase : null,
      machine: r.machine_name || r.machineName || r.machine_slug || "",
      ts: r.unixTimestamp || r.unix_timestamp || null,
    };
  }

  async function _fetchRows() {
    const api = await _api();
    // /chain/:slug/explore returns 404 today; use the live /chain/recent for both
    // modes and filter client-side. This matches the VS Code 1.287.35 wiring.
    const sf = (window.ZeqSafeFetch && window.ZeqSafeFetch.safeFetch) || null;
    const url = api + "/chain/recent?limit=80";
    try {
      const r = sf ? await sf(url, { headers: _headers() }) : { ok: true, data: await (await fetch(url, { headers: _headers() })).json() };
      if (!r.ok) {
        if (r.notDeployed) return { rows: [], note: "/api/chain/recent not deployed yet — coming online when it ships." };
        if (r.status === 401 || r.status === 403) return { rows: [], note: "Private — sign in as the owner to watch it." };
        return { rows: [], note: r.error || "Offline — retrying…" };
      }
      const j = r.data || {};
      let rows = (j.rows || j.events || j.recent || (Array.isArray(j) ? j : [])).map(_norm);
      if (_mode === "mine") {
        const s = (_slug || _mySlug() || "").toLowerCase();
        if (!s) return { rows: [], note: _authed() ? "Set your machine slug to filter." : "Sign in to watch your machine." };
        rows = rows.filter((x) => String(x.machine || "").toLowerCase().includes(s) || String(x.machine || "").toLowerCase() === s);
      }
      return { rows, note: "" };
    } catch (_) { return { rows: [], note: "Offline — retrying…" }; }
  }

  // KC.E.1 — the dedicated oracle feed (per-target cards, not raw ticks).
  async function _fetchOracles() {
    if (!_authed()) return { oracles: [], note: "Sign in to see your oracles." };
    const api = await _api();
    const s = _slug || _mySlug();
    if (!s) return { oracles: [], note: "No machine yet." };
    try {
      const r = await fetch(api + "/chain/" + encodeURIComponent(s) + "/oracles", { headers: _headers() });
      if (r.status === 401 || r.status === 403) return { oracles: [], note: "Private — sign in as the owner." };
      const j = await r.json().catch(() => ({}));
      return { oracles: Array.isArray(j.oracles) ? j.oracles : [], note: "" };
    } catch (_) { return { oracles: [], note: "Offline — retrying…" }; }
  }

  function _fmtVal(o) {
    const v = o.latest && o.latest.value !== null && o.latest.value !== undefined ? o.latest.value : null;
    if (v === null) return "—";
    const s = typeof v === "object" ? JSON.stringify(v) : String(v);
    const unit = o.unit || "";
    if (o.reading_type === "price" && unit) return unit + s;
    if (o.reading_type === "percent") return s + (unit || "%");
    return unit ? s + " " + unit : s;
  }

  function _spark(o) {
    const nums = (o.history || []).map((h) => (typeof h.value === "number" ? h.value : parseFloat(h.value))).filter((n) => Number.isFinite(n)).reverse();
    if (nums.length < 2) return "";
    const min = Math.min(...nums), max = Math.max(...nums), span = max - min || 1;
    return '<div class="zob-spark">' + nums.slice(-24).map((n) => '<i style="height:' + (10 + Math.round(((n - min) / span) * 90)) + '%"></i>').join("") + "</div>";
  }

  function _renderOracles() {
    if (!_oracles.length) {
      return '<div class="zob-ora-empty">' + esc(_status || "No oracles yet. Open any website, hit ＋Inject in the context bar, pick a value, and Start oracle — it appears here, unique per page, recording into your machine.") + "</div>";
    }
    return _oracles.map((o) =>
      '<div class="zob-ora">' +
        '<div class="zob-ora-top"><span class="zob-ora-lab">' + esc(o.label) + (o.frozen ? " · frozen" : "") + "</span>" +
        '<span class="zob-ora-host">' + esc(o.target_host || "") + "</span></div>" +
        '<div class="zob-ora-val">' + esc(_fmtVal(o)) + "</div>" +
        '<div class="zob-ora-meta"><span>' + esc(o.reading_type || "number") + "</span>" +
        "<span>" + esc(String(o.reading_count || 0)) + " readings</span>" +
        (o.latest && o.latest.at_zeqond ? "<span>z" + esc(String(o.latest.at_zeqond)) + "</span>" : "") +
        (o.interval_zeqonds ? "<span>every " + esc(String(o.interval_zeqonds)) + " Zeqonds</span>" : "") + "</div>" +
        _spark(o) +
      "</div>"
    ).join("");
  }

  const STYLE = "<style>" +
    ".zob-wrap{padding:12px;overflow-y:auto;height:100%}" +
    ".zob-modes{display:flex;gap:6px;margin-bottom:8px}" +
    ".zob-mode{flex:1;padding:7px;font-size:12px;font-weight:600;border:1px solid var(--border);border-radius:7px;background:transparent;color:var(--text-secondary);cursor:pointer}" +
    ".zob-mode.active{border-color:var(--accent-mint);color:var(--accent-mint);background:rgba(0,255,136,.06)}" +
    ".zob-watch{display:flex;gap:6px;margin-bottom:8px}" +
    ".zob-input{flex:1;padding:8px 10px;background:rgba(0,0,0,.45);border:1px solid var(--border);border-radius:7px;color:var(--text-primary);font-size:12px;outline:none}" +
    ".zob-input:focus{border-color:var(--accent-mint)}" +
    ".zob-status{font-size:10.5px;color:var(--text-muted);margin-bottom:8px;display:flex;align-items:center;gap:6px}" +
    ".zob-dot{width:7px;height:7px;border-radius:50%;background:var(--accent-mint);display:inline-block;animation:zobpulse 0.777s infinite}" +
    "@keyframes zobpulse{0%,100%{opacity:.3}50%{opacity:1}}" +
    ".zob-row{display:flex;gap:8px;align-items:baseline;padding:7px 8px;border:1px solid var(--border);border-radius:7px;margin-bottom:5px;background:var(--bg-secondary)}" +
    ".zob-type{font-size:11px;font-weight:600;color:var(--accent-mint);min-width:54px}" +
    ".zob-hash{font-family:monospace;font-size:10px;color:var(--text-secondary);word-break:break-all;flex:1}" +
    ".zob-z{font-family:monospace;font-size:9.5px;color:var(--text-muted);white-space:nowrap}" +
    ".zob-mac{font-size:10px;color:var(--text-secondary)}" +
    ".zob-flash{animation:zobflash 0.777s ease-out}" +
    "@keyframes zobflash{0%{border-color:var(--accent-mint);background:rgba(0,255,136,.14);box-shadow:0 0 0 1px var(--accent-mint) inset}100%{}}" +
    ".zob-ora{border:1px solid var(--border);border-radius:9px;padding:10px;margin-bottom:7px;background:var(--bg-secondary)}" +
    ".zob-ora-top{display:flex;align-items:baseline;justify-content:space-between;gap:8px}" +
    ".zob-ora-lab{font-size:12.5px;font-weight:700;color:var(--text-primary)}" +
    ".zob-ora-host{font-size:10px;color:var(--text-muted);font-family:monospace}" +
    ".zob-ora-val{font-family:'JetBrains Mono',monospace;font-size:19px;font-weight:800;color:var(--accent-mint);margin:4px 0 2px;word-break:break-all}" +
    ".zob-ora-meta{font-size:10px;color:var(--text-muted);display:flex;gap:8px;flex-wrap:wrap}" +
    ".zob-spark{display:flex;align-items:flex-end;gap:2px;height:24px;margin-top:6px}" +
    ".zob-spark i{flex:1;background:rgba(0,255,136,.35);border-radius:1px;min-height:2px}" +
    ".zob-ora-empty{font-size:11.5px;color:var(--text-muted);line-height:1.5}" +
    "</style>";

  function _render() {
    const c = document.getElementById(_cid);
    if (!c) return;
    let mainHtml;
    if (_mode === "oracles") {
      mainHtml = _renderOracles();
    } else {
      mainHtml = _rows.length
        ? _rows.slice(0, 50).map((r) => {
            const key = String(r.hash || r.zeqond || "");
            const isNew = key && !_seen[key];
            if (key) _seen[key] = 1;
            return '<div class="zob-row' + (isNew ? " zob-flash" : "") + '"><span class="zob-type">' + esc(r.type) + "</span>" +
              '<span class="zob-hash">' + esc(String(r.hash).slice(0, 24)) + (r.hash ? "…" : "") +
              (_mode === "network" && r.machine ? ' <span class="zob-mac">· ' + esc(r.machine) + "</span>" : "") + "</span>" +
              '<span class="zob-z">z' + esc(String(r.zeqond)) + (r.phase != null ? " φ" + r.phase.toFixed(2) : "") + "</span></div>";
          }).join("")
        : '<p style="color:var(--text-muted);font-size:12px">' + esc(_status || "No transitions yet.") + "</p>";
    }
    const shownCount = _mode === "oracles" ? _oracles.length : _rows.length;
    c.innerHTML = STYLE + '<div class="zob-wrap">' +
      '<div class="zob-modes">' +
      '<button class="zob-mode' + (_mode === "mine" ? " active" : "") + '" data-mode="mine">My machine</button>' +
      '<button class="zob-mode' + (_mode === "oracles" ? " active" : "") + '" data-mode="oracles">Oracles</button>' +
      '<button class="zob-mode' + (_mode === "network" ? " active" : "") + '" data-mode="network">Network</button></div>' +
      (_mode === "mine" ? '<div class="zob-watch"><input class="zob-input" id="zob-slug" placeholder="watch a slug (default: your machine)" value="' + esc(_slug) + '"><button class="zob-mode" style="flex:0 0 auto;padding:7px 11px" id="zob-watch-btn">Watch</button></div>' : "") +
      '<div class="zob-status"><span class="zob-dot"></span>' + esc(_status || "live · 1.287 Hz") + " · " + shownCount + (_mode === "oracles" ? " oracle(s)" : " shown") + "</div>" +
      mainHtml + "</div>";

    c.querySelectorAll(".zob-mode[data-mode]").forEach((b) => b.addEventListener("click", () => { _mode = b.getAttribute("data-mode"); _rows = []; _oracles = []; _tick(); }));
    const wb = document.getElementById("zob-watch-btn");
    if (wb) wb.addEventListener("click", () => { _slug = (document.getElementById("zob-slug").value || "").trim().toLowerCase(); _rows = []; _tick(); });
  }

  async function _tick() {
    if (!_visible()) return; // pause when tab not shown
    if (_mode === "oracles") {
      const res = await _fetchOracles();
      _oracles = res.oracles; _status = res.note || ("live · 1.287 Hz · " + (_slug || _mySlug() || "—"));
      _render();
      return;
    }
    const res = await _fetchRows();
    _rows = res.rows; _status = res.note || ("live · 1.287 Hz" + (_mode === "mine" ? " · " + (_slug || _mySlug() || "—") : " · network"));
    _render();
  }

  function init(cid) {
    _cid = cid;
    if (!_slug && _mode === "mine") _slug = "";
    _render();
    _tick();
    if (_timer) clearInterval(_timer);
    _timer = setInterval(_tick, POLL_MS);
  }
  function stop() { if (_timer) { clearInterval(_timer); _timer = null; } }

  return { init, stop };
})();

if (typeof module !== "undefined" && module.exports) { module.exports = ObserverTab; }
