/* admin/assets/js/hud_pixi.js
   Live HUD (PIXI)
   - Big gauge: ISP Download (WAN RX)
   - Text under big: ISP Upload (WAN TX)
   - Left gauges: SUM of PPP sessions Download/Upload (computed server-side as delta Mbps)
   - Right cards: WAN RX/TX/Total + System (CPU/RAM/Uptime)
   Notes:
   - Reads JSON from: hud_stats.php?ajax=1
   - Does NOT modify MikroTik connection files.
*/

(function () {
  "use strict";

  const mount = document.getElementById("uaHudStage");
  if (!mount) return;

  if (typeof PIXI === "undefined") {
    console.error("PIXI not loaded");
    return;
  }

  const clamp01 = (x) => Math.max(0, Math.min(1, x));
  const deg2rad = (d) => (d * Math.PI) / 180;


  // Color by load percentage (NOC style)
  function pickColor(pct) {
    if (pct >= 0.85) return 0xff4d4d;      // red
    if (pct >= 0.60) return 0xffc043;      // amber
    return 0x2bff88;                       // green
  }
  function pickCssColor(pct) {
    if (pct >= 0.85) return "rgba(255,77,77,.95)";
    if (pct >= 0.60) return "rgba(255,192,67,.95)";
    return "rgba(43,255,136,.95)";
  }
  const START_DEG = 135;
  const SWEEP_DEG = 270;
  const angleAt = (t) => deg2rad(START_DEG + SWEEP_DEG * t);

  function ensureOverlay() {
    if (mount.querySelector(".ua-hud-overlay")) return;

    const overlay = document.createElement("div");
    overlay.className = "ua-hud-overlay";
    overlay.innerHTML = `
      <div class="ua-hud-left">
        <div class="ua-hud-panel" id="uaPanelStatus">
          <div class="ua-hud-status">
            <div class="ua-hud-row"><span>الراوتر</span><b class="ua-hud-pill neutral" id="uaRouterState">—</b></div>
            <div class="ua-hud-row"><span>المستخدمين</span><b class="ua-hud-pill neutral" id="uaUsers">0</b></div>
            <div class="ua-hud-row"><span>الإنذارات</span><b class="ua-hud-pill warn" id="uaAlerts">0</b></div>
          </div>
        </div>

        <div class="ua-hud-panel" id="uaPanelSessUp">
          <div class="ua-hud-box-title">Sessions Upload (SUM)</div>
          <div class="ua-hud-slot" id="uaSlotSessUp"></div>
        </div>

        <div class="ua-hud-panel" id="uaPanelSessDn">
          <div class="ua-hud-box-title">Sessions Download (SUM)</div>
          <div class="ua-hud-slot" id="uaSlotSessDn"></div>
        </div>
      </div>

      <div class="ua-hud-center">
        <div class="ua-hud-panel" id="uaPanelBig">
          <div class="ua-hud-slot" id="uaSlotBig"></div>
          <div class="ua-hud-sub" id="uaIspUploadLine">ISP Upload (WAN TX): —</div>
        </div>
      </div>

      <div class="ua-hud-right">
        <div class="ua-hud-panel" id="uaPanelWan">
          <div class="ua-hud-box-title">WAN (ISP) Traffic</div>
          <div class="ua-kpi">
            <div class="ua-kpi-card ua-kpi-mini" data-kpi="wan_rx">
              <div class="ua-kpi-title">WAN RX</div>
              <div class="ua-mini-ring" id="uaWanRxRing" style="--pct:0;--ring:rgba(80,220,120,.95)">
                <div class="ua-mini-val" id="uaWanRx">0.0</div>
                <div class="ua-mini-sub">Mbps</div>
              </div>
            </div>
            <div class="ua-kpi-card ua-kpi-mini" data-kpi="wan_tx">
              <div class="ua-kpi-title">WAN TX</div>
              <div class="ua-mini-ring" id="uaWanTxRing" style="--pct:0;--ring:rgba(255,190,120,.95)">
                <div class="ua-mini-val" id="uaWanTx">0.0</div>
                <div class="ua-mini-sub">Mbps</div>
              </div>
            </div>
            <div class="ua-kpi-card ua-kpi-wide">
              <div class="ua-kpi-title">WAN Total</div>
              <div class="ua-kpi-val" id="uaWanTotal">0.0</div>
              <div class="ua-kpi-sub">Mbps (RX+TX)</div>
            </div>
          </div>
        </div>

        <div class="ua-hud-panel" id="uaPanelSys">
          <div class="ua-hud-box-title">System</div>
          <div class="ua-kpi">
            <div class="ua-kpi-card">
              <div class="ua-kpi-title">RAM</div>
              <div class="ua-kpi-val" id="uaRamPct">0</div>
              <div class="ua-kpi-sub">%</div>
            </div>
            <div class="ua-kpi-card">
              <div class="ua-kpi-title">CPU</div>
              <div class="ua-kpi-val" id="uaCpuPct">0</div>
              <div class="ua-kpi-sub">%</div>
            </div>
            <div class="ua-kpi-card ua-kpi-wide">
              <div class="ua-kpi-title">Uptime</div>
              <div class="ua-kpi-val" id="uaUptime">—</div>
              <div class="ua-kpi-sub">router</div>
            </div>
          </div>
        </div>
      </div>
    `;
    mount.appendChild(overlay);
  }

  function drawArcPolyline(g, cx, cy, r, width, t0, t1, color, alpha) {
    const STEPS = 160;
    g.lineStyle(width, color, alpha);
    const a0 = angleAt(t0);
    g.moveTo(cx + Math.cos(a0) * r, cy + Math.sin(a0) * r);
    const steps = Math.max(2, Math.floor(STEPS * Math.abs(t1 - t0)));
    for (let i = 1; i <= steps; i++) {
      const tt = t0 + (t1 - t0) * (i / steps);
      const a = angleAt(tt);
      g.lineTo(cx + Math.cos(a) * r, cy + Math.sin(a) * r);
    }
  }

  function makeArcLabels({ maxValue, radius, big }) {
    const c = new PIXI.Container();
    const steps = 6;
    for (let i = 0; i <= steps; i++) {
      const v = Math.round((maxValue * i) / steps);
      const t = i / steps;
      const a = angleAt(t);
      const rr = radius + (big ? 16 : 14);
      const tx = Math.cos(a) * rr;
      const ty = Math.sin(a) * rr;

      const txt = new PIXI.Text(String(v), {
        fill: 0xbfefff,
        fontSize: big ? 13 : 11,
        fontWeight: "900",
        fontFamily: "Tajawal, Cairo, Segoe UI, Tahoma, Arial",
      });
      txt.anchor.set(0.5);
      txt.position.set(tx, ty);
      txt.alpha = 0.65;
      c.addChild(txt);
    }
    return c;
  }

  function makeGauge({ r, label, maxValue, suffix, big }) {
    const c = new PIXI.Container();

    const base = new PIXI.Graphics();
    const ticks = new PIXI.Graphics();
    const arcBg = new PIXI.Graphics();
    const arcFg = new PIXI.Graphics();
    const needle = new PIXI.Container();
    const needleG = new PIXI.Graphics();
    const hub = new PIXI.Graphics();

    drawArcPolyline(base, 0, 0, r, big ? 10 : 9, 0, 1, 0x103240, 0.9);
    drawArcPolyline(base, 0, 0, r - (big ? 18 : 16), 2, 0, 1, 0x1a4b60, 0.55);

    const TICKS = big ? 54 : 46;
    ticks.lineStyle(2, 0x2bc1ff, 0.52);
    for (let i = 0; i <= TICKS; i++) {
      const t = i / TICKS;
      const a = angleAt(t);
      const major = i % 6 === 0;
      const len = major ? (big ? 18 : 16) : (big ? 10 : 9);
      const r1 = r - (big ? 10 : 9);
      const r2 = r1 - len;
      ticks.moveTo(Math.cos(a) * r1, Math.sin(a) * r1);
      ticks.lineTo(Math.cos(a) * r2, Math.sin(a) * r2);
    }

    const labels = makeArcLabels({ maxValue, radius: r - 6, big });
    drawArcPolyline(arcBg, 0, 0, r - (big ? 32 : 28), big ? 14 : 12, 0, 1, 0x081f2a, 0.95);

    needleG.lineStyle(big ? 5 : 4, 0xff7a3d, 0.95);
    needleG.moveTo(0, 0);
    needleG.lineTo(r - (big ? 40 : 34), 0);
    needleG.lineStyle(big ? 3 : 2, 0xff7a3d, 0.55);
    needleG.moveTo(-(big ? 18 : 14), 0);
    needleG.lineTo(0, 0);
    needle.addChild(needleG);

    hub.beginFill(0x071a22, 1);
    hub.drawCircle(0, 0, big ? 10 : 9);
    hub.endFill();
    hub.lineStyle(2, 0x2bc1ff, 0.45);
    hub.drawCircle(0, 0, big ? 12 : 11);

    const valueText = new PIXI.Text("0.0" + suffix, {
      fill: 0xffb07a,
      fontSize: big ? 44 : 22,
      fontWeight: "900",
      fontFamily: "Tajawal, Cairo, Segoe UI, Tahoma, Arial",
      letterSpacing: 0.6,
    });
    valueText.anchor.set(0.5);

    const labelText = new PIXI.Text(label, {
      fill: 0xbfefff,
      fontSize: big ? 18 : 14,
      fontWeight: "900",
      fontFamily: "Tajawal, Cairo, Segoe UI, Tahoma, Arial",
      letterSpacing: 0.4,
    });
    labelText.anchor.set(0.5);

    valueText.style.dropShadow = true;
    valueText.style.dropShadowDistance = 2;
    valueText.style.dropShadowBlur = 5;
    valueText.style.dropShadowAlpha = 0.35;

    c.addChild(arcBg, base, ticks, labels, arcFg, needle, hub, labelText, valueText);

    if (big) {
      labelText.position.set(0, r * 0.92);
      valueText.position.set(0, r * 1.14);
    } else {
      labelText.position.set(0, -r * 0.58);
      valueText.position.set(0, r * 0.66);
    }

    let current = 0,
      target = 0;

    function render(v) {
      const pct = clamp01(v / maxValue);
      const col = pickColor(pct);
      needle.rotation = angleAt(pct);

      // redraw needle to reflect load color
      needleG.clear();
      needleG.lineStyle(big ? 5 : 4, col, 0.95);
      needleG.moveTo(0, 0);
      needleG.lineTo(r - (big ? 40 : 34), 0);
      needleG.lineStyle(big ? 3 : 2, col, 0.55);
      needleG.moveTo(-(big ? 18 : 14), 0);
      needleG.lineTo(0, 0);

      arcFg.clear();
      drawArcPolyline(
        arcFg,
        0,
        0,
        r - (big ? 32 : 28),
        big ? 14 : 12,
        0,
        Math.max(0.001, pct),
        col,
        0.95
      );

      valueText.style.fill = col;
      valueText.text = (v || 0).toFixed(1) + suffix;
    }

    function setMax(newMax) {
      const m = Math.max(1, Number(newMax) || maxValue);
      maxValue = m;
    }

    function setTarget(v) {
      target = Math.max(0, Number(v) || 0);
    }
    function tick() {
      current += (target - current) * 0.14;
      if (Math.abs(target - current) < 0.05) current = target;
      render(current);
    }

    render(0);
    return { container: c, setTarget, tick, setMax, baseR: r };
  }

  ensureOverlay();

  Array.from(mount.querySelectorAll("canvas")).forEach((cv) => cv.remove());

  const app = new PIXI.Application({ resizeTo: mount, backgroundAlpha: 0, antialias: true });
  mount.prepend(app.view);

  const stage = new PIXI.Container();
  app.stage.addChild(stage);

  const gBig = makeGauge({ r: 190, label: "ISP Download (WAN RX)", maxValue: 300, suffix: " Mbps", big: true });
  const gSessUp = makeGauge({ r: 130, label: "Sessions Upload", maxValue: 300, suffix: " Mbps", big: false });
  const gSessDn = makeGauge({ r: 130, label: "Sessions Download", maxValue: 300, suffix: " Mbps", big: false });
  stage.addChild(gBig.container, gSessUp.container, gSessDn.container);

  function rectCenter(el) {
    if (!el) return null;
    const a = el.getBoundingClientRect();
    const m = mount.getBoundingClientRect();
    return { x: a.left - m.left + a.width / 2, y: a.top - m.top + a.height / 2, w: a.width, h: a.height };
  }

  function fitScale(box, gauge, extraText) {
    const margin = 18;
    const needW = gauge.baseR * 2 + margin;
    const needH = gauge.baseR * 2 + (extraText || 70);
    return Math.min(1, Math.min((box.w - 10) / needW, (box.h - 6) / needH));
  }

  function layout() {
    const bigBox = rectCenter(document.getElementById("uaSlotBig"));
    const upBox = rectCenter(document.getElementById("uaSlotSessUp"));
    const dnBox = rectCenter(document.getElementById("uaSlotSessDn"));

    if (bigBox) {
      gBig.container.position.set(bigBox.x, bigBox.y - 18);
      const s = Math.min(1, Math.min(bigBox.w, bigBox.h) / 560);
      gBig.container.scale.set(s);
    }
    if (upBox) {
      gSessUp.container.position.set(upBox.x, upBox.y + 10);
      gSessUp.container.scale.set(fitScale(upBox, gSessUp, 78));
    }
    if (dnBox) {
      gSessDn.container.position.set(dnBox.x, dnBox.y + 10);
      gSessDn.container.scale.set(fitScale(dnBox, gSessDn, 78));
    }
  }

  layout();
  window.addEventListener("resize", layout);

  const elRouter = document.getElementById("uaRouterState");
  const elUsers = document.getElementById("uaUsers");
  const elAlerts = document.getElementById("uaAlerts");
  const elWanRx = document.getElementById("uaWanRx");
  const elWanTx = document.getElementById("uaWanTx");
  const elWanTotal = document.getElementById("uaWanTotal");
  const elWanRxRing = document.getElementById("uaWanRxRing");
  const elWanTxRing = document.getElementById("uaWanTxRing");
  const elCpu = document.getElementById("uaCpuPct");
  const elRam = document.getElementById("uaRamPct");
  const elUptime = document.getElementById("uaUptime");
  const elIspUploadLine = document.getElementById("uaIspUploadLine");

  const setTxt = (el, v) => {
    if (el) el.textContent = v;

  // Smooth number animation for small KPI rings
  const kpiState = {
    wanRx: { cur: 0, target: 0 },
    wanTx: { cur: 0, target: 0 },
    wanTotal: { cur: 0, target: 0 },
    maxMbps: 300
  };
  };

  let lastOkAt = 0;
  async function pull() {
    try {
      const res = await fetch("api/hud_stats.php?ajax=1", { cache: "no-store" });
      const text = await res.text();
      let data;
      try {
        data = JSON.parse(text);
      } catch (e) {
        throw new Error("bad_json");
      }

      if (!data || !data.ok) {
        if (elRouter) {
          elRouter.textContent = "OFFLINE";
          elRouter.classList.remove("online");
          elRouter.classList.add("neutral");
        }
        return;
      }

      lastOkAt = Date.now();

      const rState = data.router && data.router.state ? String(data.router.state) : "ONLINE";
      if (elRouter) {
        elRouter.textContent = rState;
        elRouter.classList.toggle("online", rState === "ONLINE");
      }

      const users = data.users || {};
      setTxt(elUsers, String(users.ppp_active ?? users.db_online ?? 0));
      setTxt(elAlerts, String(data.alerts ?? 0));

      const kpi = data.kpi || {};
      const wanRx = Number(kpi.wan_rx_mbps ?? 0);
      const wanTx = Number(kpi.wan_tx_mbps ?? 0);
      const wanTotal = Number(kpi.wan_total_mbps ?? wanRx + wanTx);
      kpiState.wanRx.target = wanRx;
      kpiState.wanTx.target = wanTx;
      kpiState.wanTotal.target = wanTotal;
      setTxt(elCpu, String(Number(kpi.cpu_pct ?? 0).toFixed(0)));
      setTxt(elRam, String(Number(kpi.ram_pct ?? 0).toFixed(0)));
      setTxt(elUptime, String(kpi.uptime ?? "—"));

      const gauges = data.gauges || {};
      const max = Number(gauges.max_mbps ?? 300);
      kpiState.maxMbps = max;
      gBig.setMax(max);
      gSessUp.setMax(max);
      gSessDn.setMax(max);

      const ispDn = Number(gauges.isp_download_mbps ?? 0);
      const ispUp = Number(gauges.isp_upload_mbps ?? 0);
      const sessDn = Number(gauges.sessions_download_mbps ?? 0);
      const sessUp = Number(gauges.sessions_upload_mbps ?? 0);

      gBig.setTarget(ispDn);
      gSessUp.setTarget(sessUp);
      gSessDn.setTarget(sessDn);

      if (elIspUploadLine) elIspUploadLine.textContent = `ISP Upload (WAN TX): ${ispUp.toFixed(1)} Mbps`;

      // re-layout occasionally (sidebar/collapses)
      if ((Date.now() / 1000) % 10 < 2) layout();
    } catch (e) {
      if (Date.now() - lastOkAt > 8000 && elRouter) {
        elRouter.textContent = "OFFLINE";
        elRouter.classList.remove("online");
        elRouter.classList.add("neutral");
      }
    }
  }

  app.ticker.add(() => {
    gBig.tick();
    gSessUp.tick();
    gSessDn.tick();

    // animate KPI rings
    const ease = 0.18;
    const maxMbps = Math.max(1, Number(kpiState.maxMbps) || 300);

    function stepOne(st, elVal, elRing, decimals) {
      st.cur += (st.target - st.cur) * ease;
      if (Math.abs(st.target - st.cur) < 0.02) st.cur = st.target;

      if (elVal) elVal.textContent = st.cur.toFixed(decimals);

      if (elRing) {
        const pct = clamp01(st.cur / maxMbps);
        elRing.style.setProperty("--pct", String((pct * 100).toFixed(2)));
        elRing.style.setProperty("--ring", pickCssColor(pct));
      }
    }

    stepOne(kpiState.wanRx, elWanRx, elWanRxRing, 1);
    stepOne(kpiState.wanTx, elWanTx, elWanTxRing, 1);

    // total (text only)
    kpiState.wanTotal.cur += (kpiState.wanTotal.target - kpiState.wanTotal.cur) * ease;
    if (Math.abs(kpiState.wanTotal.target - kpiState.wanTotal.cur) < 0.02) kpiState.wanTotal.cur = kpiState.wanTotal.target;
    if (elWanTotal) elWanTotal.textContent = kpiState.wanTotal.cur.toFixed(1);
  });

  pull();
  setInterval(pull, 2000);

})();
