/* ============================================================
   CoinRing — the signature 3D elliptical orbit gallery.
   Panels are positioned by real orbit math each frame (rAF).
   Depth (sin of angle) drives scale / opacity / blur / z / tilt.
   Motion: slow auto-rotate + mouse-X steer + Y tilt + drag momentum.
   Renders panels imperatively (no per-frame React reconciliation).
   Exposes window.CoinRing
   ============================================================ */
(function () {
  'use strict';
  var React = window.React;

  // Responsive ring geometry
  function geom(w) {
    if (w < 480) return { N: 46, rx: 236, ry: 104, pw: 26, ph: 74, zAmp: 96,  persp: 900,  ry3d: 20 };
    if (w < 768) return { N: 58, rx: 330, ry: 122, pw: 29, ph: 86, zAmp: 118, persp: 1000, ry3d: 22 };
    if (w < 1024) return { N: 72, rx: 500, ry: 150, pw: 35, ph: 104, zAmp: 130, persp: 1150, ry3d: 24 };
    if (w < 1440) return { N: 88, rx: 646, ry: 178, pw: 39, ph: 116, zAmp: 142, persp: 1300, ry3d: 24 };
    return { N: 100, rx: 742, ry: 196, pw: 41, ph: 122, zAmp: 150, persp: 1400, ry3d: 25 };
  }

  var TAU = Math.PI * 2;
  function shortDelta(from, to) {
    var d = (to - from) % TAU;
    if (d > Math.PI) d -= TAU;
    if (d < -Math.PI) d += TAU;
    return d;
  }

  function CoinRing(props) {
    var coins = props.coins;
    var stageRef = React.useRef(null);
    var ring3dRef = React.useRef(null);
    var stateRef = React.useRef({
      offset: 0, velocity: 0.0, steer: 0, tiltX: 0,
      pointerNX: 0, pointerNY: 0, pointerActive: false,
      dragging: false, lastX: 0, dragVel: 0, hoverIdx: -1, lockIdx: -1,
      panels: [], base: [], g: geom(1200), raf: 0, running: true
    });

    // keep latest callbacks without rebuilding
    var cbRef = React.useRef(props);
    cbRef.current = props;

    React.useEffect(function () {
      var S = stateRef.current;
      var stage = stageRef.current;
      var ring = ring3dRef.current;
      var AUTO = 0.0016;

      function build() {
        var w = stage.clientWidth;
        S.g = geom(w);
        stage.style.perspective = S.g.persp + 'px';
        // clear
        ring.innerHTML = '';
        S.panels = []; S.base = [];
        var N = S.g.N;
        var frag = document.createDocumentFragment();
        for (var i = 0; i < N; i++) {
          var coin = coins[i % coins.length];
          var up = (coin.price_change_percentage_24h || 0) >= 0;
          var el = document.createElement('div');
          el.className = 'ring-panel';
          el.style.width = S.g.pw + 'px';
          el.style.height = S.g.ph + 'px';
          el.style.borderColor = up ? 'rgba(22,199,132,0.55)' : 'rgba(234,57,67,0.55)';
          el.setAttribute('role', 'button');
          el.setAttribute('aria-label', coin.name + ' ' + (up ? 'up' : 'down'));
          el.dataset.i = i;

          var img = document.createElement('img');
          img.className = 'rp-logo';
          img.loading = 'lazy';
          img.alt = '';
          img.src = coin.image;
          (function (c, imEl) {
            imEl.onerror = function () {
              imEl.style.display = 'none';
              var f = document.createElement('div');
              f.className = 'rp-fallback';
              f.textContent = c.symbol.slice(0, 1);
              f.style.background = c.color || '#6D5EF6';
              imEl.parentNode.insertBefore(f, imEl);
            };
          })(coin, img);
          el.appendChild(img);

          var sym = document.createElement('span');
          sym.className = 'rp-sym';
          sym.textContent = coin.symbol;
          el.appendChild(sym);

          var bar = document.createElement('span');
          bar.className = 'rp-bar';
          bar.style.background = up ? 'var(--color-up)' : 'var(--color-down)';
          el.appendChild(bar);

          (function (idx, c) {
            el.addEventListener('mouseenter', function () {
              if (S.dragging) return;
              S.hoverIdx = idx;
              if (cbRef.current.onHover) cbRef.current.onHover(c);
            });
            el.addEventListener('mouseleave', function () {
              if (S.hoverIdx === idx) {
                S.hoverIdx = -1;
                if (cbRef.current.onHover) cbRef.current.onHover(null);
              }
            });
            el.addEventListener('click', function (ev) {
              ev.stopPropagation();
              S.lockIdx = idx;
              if (cbRef.current.onLock) cbRef.current.onLock(c, idx);
            });
          })(i, coin);

          frag.appendChild(el);
          S.panels.push(el);
          S.base.push((i / N) * TAU);
        }
        ring.appendChild(frag);
      }

      function frame() {
        if (!S.running) return;
        var g = S.g;
        if (S.lockIdx >= 0) {
          var target = (Math.PI / 2) - S.base[S.lockIdx];
          S.offset += shortDelta(S.offset, target) * 0.12;
          S.velocity = 0;
        } else if (!S.dragging) {
          S.offset += S.velocity + AUTO;
          S.velocity *= 0.94;
        }
        // steer + tilt easing
        var steerTarget = (S.pointerActive && !S.dragging && S.lockIdx < 0) ? S.pointerNX * 0.42 : 0;
        S.steer += (steerTarget - S.steer) * 0.05;
        var tiltTarget = S.pointerActive ? S.pointerNY * 6.5 : 0;
        S.tiltX += (tiltTarget - S.tiltX) * 0.06;

        ring.style.transform = 'rotateX(' + (-S.tiltX) + 'deg)';

        var ro = S.offset + S.steer;
        var panels = S.panels, base = S.base, n = panels.length;
        for (var i = 0; i < n; i++) {
          var a = base[i] + ro;
          var c = Math.cos(a), s = Math.sin(a);
          var t = (s + 1) / 2;                 // 0 back .. 1 front
          var x = c * g.rx;
          var y = s * g.ry;
          var z = s * g.zAmp;
          var scale = 0.46 + t * 0.66;
          var ry = -c * g.ry3d;
          var bright = 0.60 + t * 0.42;
          var blur = (1 - t) * 1.5;
          var op = 0.24 + t * 0.76;
          if (i === S.hoverIdx) { scale *= 1.14; bright = Math.max(bright, 1.08); blur = 0; op = 1; }
          var el = panels[i];
          el.style.transform = 'translate(-50%,-50%) translate3d(' + x.toFixed(1) + 'px,' + y.toFixed(1) + 'px,' + z.toFixed(1) + 'px) scale(' + scale.toFixed(3) + ') rotateY(' + ry.toFixed(1) + 'deg)';
          el.style.opacity = op.toFixed(2);
          el.style.filter = (blur > 0.06 ? 'blur(' + blur.toFixed(2) + 'px) ' : '') + 'brightness(' + bright.toFixed(2) + ')';
          el.style.zIndex = String(Math.round(t * 200));
        }
        S.raf = requestAnimationFrame(frame);
      }

      // ---- Pointer handlers ----
      function onMove(e) {
        var r = stage.getBoundingClientRect();
        var px = (e.clientX - r.left) / r.width;
        var py = (e.clientY - r.top) / r.height;
        S.pointerNX = (px - 0.5) * 2;
        S.pointerNY = (py - 0.5) * 2;
        S.pointerActive = true;
        if (S.dragging) {
          var dx = e.clientX - S.lastX;
          S.offset += dx * 0.006;
          S.dragVel = dx * 0.006;
          S.lastX = e.clientX;
          if (S.lockIdx >= 0) { S.lockIdx = -1; if (cbRef.current.onUnlock) cbRef.current.onUnlock(); }
        }
      }
      function onDown(e) {
        S.dragging = true;
        S.lastX = e.clientX;
        S.dragVel = 0;
        stage.classList.add('grabbing');
        if (cbRef.current.onDragStart) cbRef.current.onDragStart();
      }
      function onUp() {
        if (!S.dragging) return;
        S.dragging = false;
        stage.classList.remove('grabbing');
        // hand momentum to velocity (clamped)
        var v = S.dragVel;
        S.velocity = Math.max(-0.05, Math.min(0.05, v));
      }
      function onLeave() {
        S.pointerActive = false;
        onUp();
      }

      build();
      stage.addEventListener('pointermove', onMove);
      stage.addEventListener('pointerdown', onDown);
      window.addEventListener('pointerup', onUp);
      stage.addEventListener('pointerleave', onLeave);

      var ro = new ResizeObserver(function () {
        var w = stage.clientWidth;
        var ng = geom(w);
        if (ng.N !== S.g.N) build(); else { S.g = ng; stage.style.perspective = ng.persp + 'px'; }
      });
      ro.observe(stage);

      S.running = true;
      S.raf = requestAnimationFrame(frame);

      return function () {
        S.running = false;
        cancelAnimationFrame(S.raf);
        ro.disconnect();
        stage.removeEventListener('pointermove', onMove);
        stage.removeEventListener('pointerdown', onDown);
        window.removeEventListener('pointerup', onUp);
        stage.removeEventListener('pointerleave', onLeave);
      };
    }, [coins]);

    // expose unlock to parent (e.g. clicking backdrop)
    React.useImperativeHandle(props.handle, function () {
      return {
        unlock: function () { stateRef.current.lockIdx = -1; },
        spinTo: function (idx) { stateRef.current.lockIdx = idx; }
      };
    });

    return React.createElement('div', {
      className: 'ring-stage', ref: stageRef,
      onClick: function () { /* click empty area unlocks */
        var S = stateRef.current;
        if (S.hoverIdx < 0 && S.lockIdx >= 0) { S.lockIdx = -1; if (props.onUnlock) props.onUnlock(); }
      }
    }, React.createElement('div', { className: 'ring-3d', ref: ring3dRef }));
  }

  window.CoinRing = CoinRing;
})();
