/* global React */
/*
 * Codartia — Planet System
 * Obsidian-graph-inspired force-directed orbital animation.
 *
 * Each planet is connected to the sun by a spring.
 * Planets repel each other. Drag adds velocity. On release,
 * they spring back toward their orbital target (which slowly
 * advances around the sun -> creates the orbiting motion).
 */
(function () {
  const { useEffect, useRef, useState, useMemo } = React;

  function PlanetSystem({
    products,
    sunColor,
    orbitSpeed = 1,
    starDensity = 1,
    onSelect,
    language = "pt",
    theme = "dark",
  }) {
    const canvasRef = useRef(null);
    const containerRef = useRef(null);
    const planetsRef = useRef([]);
    const sunRef = useRef({ x: 0, y: 0, r: 0, pulse: 0 });
    const interactionRef = useRef({
      mouse: { x: 0, y: 0, inside: false },
      dragging: null,
      dragOffset: { x: 0, y: 0 },
      hover: null,
      lastClickPlanet: null,
      pressStart: 0,
      pressPos: null,
    });
    const dimsRef = useRef({ w: 0, h: 0, scale: 1 });
    const timeRef = useRef(0);

    const [tooltip, setTooltip] = useState(null); // { x, y, product }
    const [cursor, setCursor] = useState("default");

    // ----- Setup planets once products change -----
    useEffect(() => {
      const n = products.length;
      planetsRef.current = products.map((p, i) => {
        // distribute on 2-3 rings for visual depth, with more breathing room
        const ringCount = Math.min(3, Math.max(2, Math.ceil(n / 6)));
        const ring = i % ringCount;
        const inRing = Math.floor(i / ringCount);
        const perRing = Math.ceil(n / ringCount);
        const baseR = 0.46 + ring * 0.18; // fraction of min dim (kept inside stage)
        const startAngle = (Math.PI * 2 * inRing) / perRing + ring * 0.6 + i * 0.07;
        return {
          id: p.id,
          product: p,
          // orbital target
          targetR: baseR,
          angle: startAngle,
          // angular speed (rad/sec), alternating direction by ring
          omega: (0.05 + ring * 0.015) * (ring % 2 === 0 ? 1 : -1),
          // physics state — initial position at target
          x: 0, y: 0, vx: 0, vy: 0,
          // radius in pixels
          rPx: 18 + (p.size || 1) * 8,
          // visual
          color: p.color,
          glyph: p.glyph,
          name: p.name,
          // bookkeeping
          hover: 0,
          ring,
        };
      });
    }, [products]);

    // ----- Resize handling -----
    useEffect(() => {
      const onResize = () => {
        const el = containerRef.current;
        if (!el) return;
        const r = el.getBoundingClientRect();
        const dpr = Math.min(2, window.devicePixelRatio || 1);
        const c = canvasRef.current;
        c.width = Math.floor(r.width * dpr);
        c.height = Math.floor(r.height * dpr);
        c.style.width = r.width + "px";
        c.style.height = r.height + "px";
        const ctx = c.getContext("2d");
        ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
        dimsRef.current = { w: r.width, h: r.height, scale: dpr };
        // sun in center
        const min = Math.min(r.width, r.height);
        sunRef.current = { x: r.width / 2, y: r.height / 2, r: min * 0.085, pulse: 0 };
        // seed planet positions on first run
        planetsRef.current.forEach((p) => {
          if (p.x === 0 && p.y === 0) {
            const radius = p.targetR * min * 0.5;
            p.x = sunRef.current.x + Math.cos(p.angle) * radius;
            p.y = sunRef.current.y + Math.sin(p.angle) * radius;
          }
        });
      };
      onResize();
      const ro = new ResizeObserver(onResize);
      if (containerRef.current) ro.observe(containerRef.current);
      window.addEventListener("resize", onResize);
      return () => {
        ro.disconnect();
        window.removeEventListener("resize", onResize);
      };
    }, []);

    // ----- Stars (background dust) -----
    const stars = useMemo(() => {
      const count = Math.floor(120 * starDensity);
      return Array.from({ length: count }, () => ({
        rx: Math.random(), // 0..1 relative
        ry: Math.random(),
        r: Math.random() * 1.2 + 0.2,
        a: Math.random() * 0.6 + 0.2,
        tw: Math.random() * Math.PI * 2,
      }));
    }, [starDensity]);

    // ----- Mouse / touch -----
    useEffect(() => {
      const c = canvasRef.current;
      if (!c) return;

      const getPos = (e) => {
        const r = c.getBoundingClientRect();
        const pt = e.touches ? e.touches[0] : e;
        return { x: pt.clientX - r.left, y: pt.clientY - r.top };
      };

      const planetAt = (x, y) => {
        // pick topmost (last drawn first)
        const arr = planetsRef.current;
        for (let i = arr.length - 1; i >= 0; i--) {
          const p = arr[i];
          const dx = p.x - x, dy = p.y - y;
          if (dx * dx + dy * dy <= (p.rPx + 6) * (p.rPx + 6)) return p;
        }
        return null;
      };

      const onMove = (e) => {
        const pos = getPos(e);
        const I = interactionRef.current;
        I.mouse = { x: pos.x, y: pos.y, inside: true };
        if (I.dragging) {
          I.dragging.x = pos.x - I.dragOffset.x;
          I.dragging.y = pos.y - I.dragOffset.y;
          I.dragging.vx = 0;
          I.dragging.vy = 0;
        } else {
          const p = planetAt(pos.x, pos.y);
          I.hover = p;
          setCursor(p ? "pointer" : "default");
          if (p) {
            setTooltip({ x: p.x, y: p.y - p.rPx - 10, product: p.product });
          } else {
            setTooltip(null);
          }
        }
      };

      const onDown = (e) => {
        const pos = getPos(e);
        const I = interactionRef.current;
        const p = planetAt(pos.x, pos.y);
        I.pressStart = Date.now();
        I.pressPos = pos;
        if (p) {
          I.dragging = p;
          I.dragOffset = { x: pos.x - p.x, y: pos.y - p.y };
          I.lastClickPlanet = p;
          setCursor("grabbing");
          e.preventDefault();
        }
      };

      const onUp = (e) => {
        const I = interactionRef.current;
        const wasDragging = I.dragging;
        const pos = I.mouse;
        const dx = I.pressPos ? pos.x - I.pressPos.x : 99;
        const dy = I.pressPos ? pos.y - I.pressPos.y : 99;
        const moved = Math.hypot(dx, dy);
        const dt = Date.now() - I.pressStart;
        I.dragging = null;
        setCursor(I.hover ? "pointer" : "default");
        // click (short press, little movement)
        if (wasDragging && moved < 6 && dt < 350) {
          if (onSelect) onSelect(wasDragging.product);
        }
      };

      const onLeave = () => {
        const I = interactionRef.current;
        I.mouse.inside = false;
        I.dragging = null;
        I.hover = null;
        setTooltip(null);
        setCursor("default");
      };

      c.addEventListener("mousemove", onMove);
      c.addEventListener("mousedown", onDown);
      window.addEventListener("mouseup", onUp);
      c.addEventListener("mouseleave", onLeave);
      c.addEventListener("touchstart", onDown, { passive: false });
      c.addEventListener("touchmove", onMove, { passive: false });
      c.addEventListener("touchend", onUp);
      return () => {
        c.removeEventListener("mousemove", onMove);
        c.removeEventListener("mousedown", onDown);
        window.removeEventListener("mouseup", onUp);
        c.removeEventListener("mouseleave", onLeave);
        c.removeEventListener("touchstart", onDown);
        c.removeEventListener("touchmove", onMove);
        c.removeEventListener("touchend", onUp);
      };
    }, [onSelect]);

    // ----- Physics + render loop -----
    useEffect(() => {
      let raf;
      let last = performance.now();
      const tick = (now) => {
        const dt = Math.min(0.05, (now - last) / 1000);
        last = now;
        step(dt * orbitSpeed);
        draw();
        raf = requestAnimationFrame(tick);
      };
      raf = requestAnimationFrame(tick);
      return () => cancelAnimationFrame(raf);
    }, [orbitSpeed, sunColor, theme, starDensity]);

    function step(dt) {
      timeRef.current += dt;
      const { w, h } = dimsRef.current;
      const min = Math.min(w, h);
      const sun = sunRef.current;
      sun.pulse = (Math.sin(timeRef.current * 1.2) + 1) * 0.5;

      const arr = planetsRef.current;
      const I = interactionRef.current;

      // Sun position update (could be made interactive later)
      sun.x = w / 2;
      sun.y = h / 2;
      sun.r = min * 0.085;

      // 1) advance orbit angles
      for (const p of arr) {
        p.angle += p.omega * dt;
      }

      // 2) compute forces
      for (let i = 0; i < arr.length; i++) {
        const p = arr[i];
        if (I.dragging === p) continue; // skip physics on dragged
        const targetRadius = p.targetR * min * 0.5;
        const tx = sun.x + Math.cos(p.angle) * targetRadius;
        const ty = sun.y + Math.sin(p.angle) * targetRadius;

        // Spring toward target
        const k = 4.0; // stiffness
        const ax = (tx - p.x) * k;
        const ay = (ty - p.y) * k;
        p.vx += ax * dt;
        p.vy += ay * dt;

        // Repulsion from other planets (Coulomb-ish)
        for (let j = 0; j < arr.length; j++) {
          if (i === j) continue;
          const q = arr[j];
          let dx = p.x - q.x;
          let dy = p.y - q.y;
          let d2 = dx * dx + dy * dy;
          const minD = (p.rPx + q.rPx) * 1.6;
          if (d2 < minD * minD) {
            const d = Math.sqrt(d2) || 0.01;
            const overlap = minD - d;
            const nx = dx / d;
            const ny = dy / d;
            const force = overlap * 8;
            p.vx += nx * force * dt;
            p.vy += ny * force * dt;
          }
        }

        // Sun repulsion (don't fall in)
        const sdx = p.x - sun.x;
        const sdy = p.y - sun.y;
        const sd2 = sdx * sdx + sdy * sdy;
        const sunMin = sun.r + p.rPx + 12;
        if (sd2 < sunMin * sunMin) {
          const d = Math.sqrt(sd2) || 0.01;
          const overlap = sunMin - d;
          p.vx += (sdx / d) * overlap * 20 * dt;
          p.vy += (sdy / d) * overlap * 20 * dt;
        }

        // Damping
        const damp = 2.4;
        p.vx -= p.vx * damp * dt;
        p.vy -= p.vy * damp * dt;

        // Integrate
        p.x += p.vx * dt;
        p.y += p.vy * dt;
      }

      // hover smoothing
      for (const p of arr) {
        const target = (I.hover === p || I.dragging === p) ? 1 : 0;
        p.hover += (target - p.hover) * Math.min(1, dt * 8);
      }
    }

    function draw() {
      const c = canvasRef.current;
      if (!c) return;
      const ctx = c.getContext("2d");
      const { w, h } = dimsRef.current;
      ctx.clearRect(0, 0, w, h);

      const sun = sunRef.current;
      const arr = planetsRef.current;
      const isDark = theme !== "light";

      // ----- Stars -----
      const starColor = isDark ? "255,255,255" : "20,16,42";
      for (const s of stars) {
        const x = s.rx * w;
        const y = s.ry * h;
        const tw = (Math.sin(timeRef.current * 1.5 + s.tw) + 1) * 0.3 + 0.5;
        ctx.globalAlpha = s.a * tw * (isDark ? 1 : 0.4);
        ctx.fillStyle = `rgba(${starColor}, 1)`;
        ctx.beginPath();
        ctx.arc(x, y, s.r, 0, Math.PI * 2);
        ctx.fill();
      }
      ctx.globalAlpha = 1;

      // ----- Orbit rings (very subtle guides) -----
      const min = Math.min(w, h);
      const uniqueR = [...new Set(arr.map(p => p.targetR))];
      ctx.strokeStyle = isDark ? "rgba(167, 139, 250, 0.06)" : "rgba(108, 70, 224, 0.10)";
      ctx.lineWidth = 1;
      for (const tr of uniqueR) {
        ctx.beginPath();
        ctx.arc(sun.x, sun.y, tr * min * 0.5, 0, Math.PI * 2);
        ctx.stroke();
      }

      // ----- Edges (sun → planet) -----
      for (const p of arr) {
        const grad = ctx.createLinearGradient(sun.x, sun.y, p.x, p.y);
        const dist = Math.hypot(p.x - sun.x, p.y - sun.y);
        const alpha = 0.25 + p.hover * 0.5;
        grad.addColorStop(0, hexToRgba(sunColor, alpha * 0.7));
        grad.addColorStop(1, hexToRgba(p.color, alpha));
        ctx.strokeStyle = grad;
        ctx.lineWidth = 1 + p.hover * 1.5;
        ctx.beginPath();
        ctx.moveTo(sun.x, sun.y);
        ctx.lineTo(p.x, p.y);
        ctx.stroke();
      }

      // ----- Sun -----
      // outer glow
      const sunGlow = ctx.createRadialGradient(sun.x, sun.y, sun.r * 0.4, sun.x, sun.y, sun.r * 4);
      sunGlow.addColorStop(0, hexToRgba(sunColor, 0.55));
      sunGlow.addColorStop(0.4, hexToRgba(sunColor, 0.15));
      sunGlow.addColorStop(1, hexToRgba(sunColor, 0));
      ctx.fillStyle = sunGlow;
      ctx.beginPath();
      ctx.arc(sun.x, sun.y, sun.r * 4, 0, Math.PI * 2);
      ctx.fill();

      // corona pulse
      const pulseR = sun.r * (1.4 + sun.pulse * 0.15);
      ctx.strokeStyle = hexToRgba(sunColor, 0.18);
      ctx.lineWidth = 2;
      ctx.beginPath();
      ctx.arc(sun.x, sun.y, pulseR, 0, Math.PI * 2);
      ctx.stroke();

      // sun body
      const sunBody = ctx.createRadialGradient(
        sun.x - sun.r * 0.3, sun.y - sun.r * 0.3, sun.r * 0.1,
        sun.x, sun.y, sun.r
      );
      sunBody.addColorStop(0, "#FFFFFF");
      sunBody.addColorStop(0.3, lighten(sunColor, 0.3));
      sunBody.addColorStop(1, sunColor);
      ctx.fillStyle = sunBody;
      ctx.beginPath();
      ctx.arc(sun.x, sun.y, sun.r, 0, Math.PI * 2);
      ctx.fill();

      // sun label
      ctx.fillStyle = isDark ? "rgba(255,255,255,0.95)" : "rgba(20,16,42,0.95)";
      ctx.font = `600 ${Math.max(11, sun.r * 0.28)}px "JetBrains Mono", monospace`;
      ctx.textAlign = "center";
      ctx.textBaseline = "middle";
      // overlay label on top of sun (white due to glow contrast)
      ctx.fillStyle = "#FFFFFF";
      ctx.shadowColor = "rgba(0,0,0,0.4)";
      ctx.shadowBlur = 4;
      ctx.fillText("CODARTIA", sun.x, sun.y);
      ctx.shadowBlur = 0;

      // ----- Planets -----
      for (const p of arr) {
        const r = p.rPx * (1 + p.hover * 0.18);

        // glow
        const glow = ctx.createRadialGradient(p.x, p.y, r * 0.2, p.x, p.y, r * 3);
        glow.addColorStop(0, hexToRgba(p.color, 0.35 + p.hover * 0.3));
        glow.addColorStop(1, hexToRgba(p.color, 0));
        ctx.fillStyle = glow;
        ctx.beginPath();
        ctx.arc(p.x, p.y, r * 3, 0, Math.PI * 2);
        ctx.fill();

        // body
        const body = ctx.createRadialGradient(
          p.x - r * 0.35, p.y - r * 0.35, r * 0.05,
          p.x, p.y, r
        );
        body.addColorStop(0, lighten(p.color, 0.45));
        body.addColorStop(0.5, p.color);
        body.addColorStop(1, darken(p.color, 0.35));
        ctx.fillStyle = body;
        ctx.beginPath();
        ctx.arc(p.x, p.y, r, 0, Math.PI * 2);
        ctx.fill();

        // rim ring
        ctx.strokeStyle = hexToRgba(lighten(p.color, 0.4), 0.6);
        ctx.lineWidth = 1;
        ctx.beginPath();
        ctx.arc(p.x, p.y, r + 0.5, 0, Math.PI * 2);
        ctx.stroke();

        // glyph (mono letters)
        ctx.fillStyle = "rgba(255,255,255,0.95)";
        ctx.font = `700 ${r * 0.6}px "JetBrains Mono", monospace`;
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        ctx.shadowColor = "rgba(0,0,0,0.35)";
        ctx.shadowBlur = 3;
        ctx.fillText(p.glyph, p.x, p.y);
        ctx.shadowBlur = 0;

        // label below
        const labelAlpha = 0.55 + p.hover * 0.45;
        ctx.fillStyle = isDark
          ? `rgba(241, 236, 255, ${labelAlpha})`
          : `rgba(21, 16, 43, ${labelAlpha})`;
        ctx.font = `500 ${11 + p.hover * 1.5}px Sora, sans-serif`;
        ctx.fillText(p.name, p.x, p.y + r + 14);
      }
    }

    return (
      <div ref={containerRef} className="planet-stage" style={{ cursor }}>
        <canvas ref={canvasRef} />
        {tooltip && (
          <div
            className={`planet-tooltip ${tooltip ? "show" : ""}`}
            style={{ left: tooltip.x, top: tooltip.y }}
          >
            <div className="tt-name">
              <span className="tt-dot" style={{ background: tooltip.product.color }} />
              {tooltip.product.name}
            </div>
            <div className="tt-tag">{tooltip.product.tagline[language]}</div>
            <div className="tt-cta">
              {language === "pt" ? "Clique para abrir →" : "Click to open →"}
            </div>
          </div>
        )}
      </div>
    );
  }

  // ===== Color helpers =====
  function hexToRgba(hex, a) {
    const h = hex.replace("#", "");
    const r = parseInt(h.substring(0, 2), 16);
    const g = parseInt(h.substring(2, 4), 16);
    const b = parseInt(h.substring(4, 6), 16);
    return `rgba(${r}, ${g}, ${b}, ${a})`;
  }
  function lighten(hex, amt) {
    const h = hex.replace("#", "");
    const r = parseInt(h.substring(0, 2), 16);
    const g = parseInt(h.substring(2, 4), 16);
    const b = parseInt(h.substring(4, 6), 16);
    const lr = Math.round(r + (255 - r) * amt);
    const lg = Math.round(g + (255 - g) * amt);
    const lb = Math.round(b + (255 - b) * amt);
    return `rgb(${lr}, ${lg}, ${lb})`;
  }
  function darken(hex, amt) {
    const h = hex.replace("#", "");
    const r = parseInt(h.substring(0, 2), 16);
    const g = parseInt(h.substring(2, 4), 16);
    const b = parseInt(h.substring(4, 6), 16);
    return `rgb(${Math.round(r * (1 - amt))}, ${Math.round(g * (1 - amt))}, ${Math.round(b * (1 - amt))})`;
  }

  window.PlanetSystem = PlanetSystem;
})();
