
/* admin/assets/js/mini_gauges.js
   Two small gauges ONLY (keep original gauge design).
   Reads JSON from: api/noc_stats.php?ajax=1
*/
(function () {
  "use strict";
  const mountUp = document.getElementById("uaMiniSessUp");
  const mountDn = document.getElementById("uaMiniSessDn");
  if (!mountUp || !mountDn) 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;

  const START_DEG = 135;
  const SWEEP_DEG = 270;
  const angleAt = (t) => deg2rad(START_DEG + SWEEP_DEG * t);

  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 = big ? 6 : 5; // keep original density
    for (let i = 0; i <= steps; i++) {
      const t = i / steps;
      const a = angleAt(t);
      const v = Math.round(maxValue * t);
      const tx = Math.cos(a) * (radius + (big ? 22 : 18));
      const ty = Math.sin(a) * (radius + (big ? 22 : 18));

      const label = new PIXI.Text(String(v), {
        fontFamily: "system-ui, -apple-system, Segoe UI, Roboto, Arial",
        fontSize: big ? 14 : 12,
        fill: 0xb8c8d6,
      });
      label.anchor.set(0.5);
      label.position.set(tx, ty);
      c.addChild(label);
    }
    return c;
  }

  function formatValue(v, suffix) {
    if (!isFinite(v)) v = 0;
    if (Math.abs(v) >= 100) return String(Math.round(v)) + suffix;
    if (Math.abs(v) >= 10) return v.toFixed(1) + suffix;
    return v.toFixed(2) + suffix;
  }

  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, 0x0a1f2a, 1);

    // ticks
    const tickCount = big ? 48 : 44;
    for (let i = 0; i <= tickCount; i++) {
      const t = i / tickCount;
      const a = angleAt(t);
      const isMajor = i % (big ? 8 : 11) === 0;
      const r0 = r - (big ? 18 : 16);
      const r1 = r0 - (isMajor ? 10 : 6);
      ticks.lineStyle(isMajor ? 2 : 1, isMajor ? 0xb8c8d6 : 0x385363, isMajor ? 0.9 : 0.55);
      ticks.moveTo(Math.cos(a) * r0, Math.sin(a) * r0);
      ticks.lineTo(Math.cos(a) * r1, Math.sin(a) * r1);
    }

    // arc background + foreground
    drawArcPolyline(arcBg, 0, 0, r - (big ? 32 : 28), big ? 12 : 10, 0, 1, 0x0e2a36, 0.9);
    // arcFg drawn per update

    // needle
    needleG.beginFill(0xffcc33, 0.95);
    needleG.drawRoundedRect(-2, -r + (big ? 44 : 40), 4, r - (big ? 58 : 52), 2);
    needleG.endFill();
    needle.addChild(needleG);

    hub.beginFill(0xffcc33, 0.95);
    hub.drawCircle(0, 0, big ? 8 : 7);
    hub.endFill();

    // texts
    const labelText = new PIXI.Text(label, {
      fontFamily: "system-ui, -apple-system, Segoe UI, Roboto, Arial",
      fontSize: big ? 16 : 14,
      fill: 0xf0f6ff,
      fontWeight: "700",
    });
    labelText.anchor.set(0.5);
    labelText.position.set(0, big ? -10 : -8);

    const valueText = new PIXI.Text("0" + suffix, {
      fontFamily: "system-ui, -apple-system, Segoe UI, Roboto, Arial",
      fontSize: big ? 30 : 26,
      fill: 0xffcc33,
      fontWeight: "800",
    });
    valueText.anchor.set(0.5);
    valueText.position.set(0, big ? 24 : 22);

    const subText = new PIXI.Text("Mbps", {
      fontFamily: "system-ui, -apple-system, Segoe UI, Roboto, Arial",
      fontSize: 12,
      fill: 0xb8c8d6,
    });
    subText.anchor.set(0.5);
    subText.position.set(0, big ? 48 : 44);

    const labels = makeArcLabels({ maxValue, radius: r - (big ? 8 : 6), big });

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

    let current = 0;

    function update(v, newMax) {
      if (!isFinite(v)) v = 0;
      if (isFinite(newMax) && newMax > 0) maxValue = newMax;

      // smooth
      current = current + (v - current) * 0.22;

      const t = clamp01(maxValue > 0 ? current / maxValue : 0);
      const a = angleAt(t);
      needle.rotation = a + Math.PI / 2;

      // redraw foreground arc
      arcFg.clear();
      drawArcPolyline(arcFg, 0, 0, r - (big ? 32 : 28), big ? 12 : 10, 0, t, 0xffcc33, 0.95);

      valueText.text = formatValue(current, suffix);
    }

    return { container: c, update };
  }

  function makeStage(mountEl) {
    const w = mountEl.clientWidth || 320;
    const h = mountEl.clientHeight || 210;
    const app = new PIXI.Application({
      width: w,
      height: h,
      backgroundAlpha: 0,
      antialias: true,
      resolution: window.devicePixelRatio || 1,
      autoDensity: true,
    });
    mountEl.innerHTML = "";
    mountEl.appendChild(app.view);
    return app;
  }

  const appUp = makeStage(mountUp);
  const appDn = makeStage(mountDn);

  const gUp = makeGauge({ r: 82, label: "Sessions Upload", maxValue: 300, suffix: "", big: false });
  const gDn = makeGauge({ r: 82, label: "Sessions Download", maxValue: 300, suffix: "", big: false });

  gUp.container.position.set(appUp.renderer.width / 2, appUp.renderer.height / 2 + 10);
  gDn.container.position.set(appDn.renderer.width / 2, appDn.renderer.height / 2 + 10);

  appUp.stage.addChild(gUp.container);
  appDn.stage.addChild(gDn.container);

  async function tick() {
    try {
      const r = await fetch("api/noc_stats.php?ajax=1", { cache: "no-store" });
      const txt = await r.text();
      let data = null;
      try { data = JSON.parse(txt); } catch (e) { data = null; }

      if (!data || !data.ok) return;

      const up = Number(data.gauges?.sessions_upload_mbps ?? 0);
      const dn = Number(data.gauges?.sessions_download_mbps ?? 0);
      const mx = Number(data.gauges?.max_mbps ?? 300) || 300;

      gUp.update(up, mx);
      gDn.update(dn, mx);
    } catch (e) {
      // silent
    }
  }

  // render loop
  appUp.ticker.add(() => tickOncePerFrame(appUp, tick));
  appDn.ticker.add(() => tickOncePerFrame(appDn, tick));

  let _last = 0;
  function tickOncePerFrame(app, fn) {
    const now = performance.now();
    if (now - _last < 1000) return;
    _last = now;
    fn();
  }

  // also poll
  setInterval(tick, 2000);
  tick();

  // resize
  function onResize() {
    for (const [app, mountEl, g] of [[appUp, mountUp, gUp],[appDn, mountDn, gDn]]) {
      const w = mountEl.clientWidth || 320;
      const h = mountEl.clientHeight || 210;
      app.renderer.resize(w, h);
      g.container.position.set(w/2, h/2 + 10);
    }
  }
  window.addEventListener("resize", onResize);
})();
