/* Hero animated background — dotted Earth + teal accent + particles.
 * Loads an equirectangular Earth texture, samples land mask per Fibonacci point,
 * and renders the globe rotating slowly. Self-contained, looping.
 */
/* global React */
const { useEffect, useRef } = React;

function HeroBackground() {
  const canvasRef = useRef(null);
  const rafRef = useRef(0);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    let W = 0, H = 0;
    const dpr = Math.min(window.devicePixelRatio || 1, 2);

    function resize() {
      const r = canvas.getBoundingClientRect();
      W = r.width; H = r.height;
      canvas.width = Math.floor(W * dpr);
      canvas.height = Math.floor(H * dpr);
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    }
    resize();
    const ro = new ResizeObserver(resize);
    ro.observe(canvas);

    /* ---------- Globe geometry ---------- */
    const N = 40000;
    const golden = Math.PI * (3 - Math.sqrt(5));
    const points = [];
    for (let i = 0; i < N; i++) {
      const y = 1 - (i / (N - 1)) * 2;
      const r = Math.sqrt(1 - y * y);
      const theta = golden * i;
      const x = Math.cos(theta) * r;
      const z = Math.sin(theta) * r;
      const lat = Math.asin(y);
      const lng = Math.atan2(z, x);
      points.push({ x, y, z, lat, lng });
    }

    /* ---------- Land mask: sample equirectangular Earth texture ----------
     * We try a couple of CORS-friendly URLs. The map is sampled into a
     * 720x360 offscreen canvas; per point we lookup (lng, lat) and decide
     * land vs sea by brightness (or "not blue"). Until the texture loads we
     * render every point faintly so something is on screen.
     */
    let landFlags = null;
    const TEX_W = 1440, TEX_H = 720;

    function tryLoad(urls, onSuccess, onAllFail) {
      let idx = 0;
      const attempt = () => {
        if (idx >= urls.length) { onAllFail && onAllFail(); return; }
        const img = new Image();
        img.crossOrigin = "anonymous";
        img.onload = () => onSuccess(img);
        img.onerror = () => { idx++; attempt(); };
        img.src = urls[idx];
      };
      attempt();
    }

    tryLoad(
      [
        "https://unpkg.com/three-globe@2.27.1/example/img/earth-topology.png",
        "https://cdn.jsdelivr.net/npm/three-globe@2.27.1/example/img/earth-topology.png",
        "https://unpkg.com/three-globe@2.27.1/example/img/earth-blue-marble.jpg",
        "https://cdn.jsdelivr.net/npm/three-globe@2.27.1/example/img/earth-blue-marble.jpg",
        "https://unpkg.com/three-globe@2.27.1/example/img/earth-day.jpg",
      ],
      (img) => {
        try {
          const off = document.createElement("canvas");
          off.width = TEX_W; off.height = TEX_H;
          const oc = off.getContext("2d");
          oc.drawImage(img, 0, 0, TEX_W, TEX_H);
          const data = oc.getImageData(0, 0, TEX_W, TEX_H).data;

          // Detect texture type by sampling a known-ocean pixel (mid-Pacific ~ lat 0, lng -150)
          // Pacific center: u = ((-150 + 180) / 360) = 0.0833
          const probeX = Math.floor(0.0833 * TEX_W);
          const probeY = Math.floor(0.5 * TEX_H);
          const pk = (probeY * TEX_W + probeX) * 4;
          const pr = data[pk], pg = data[pk + 1], pb = data[pk + 2];
          // If ocean pixel is very dark, treat as topology (grayscale, dark=sea, light=land)
          // Otherwise treat as blue-marble style.
          const isTopology = (pr + pg + pb) / 3 < 60 && Math.abs(pr - pb) < 25;

          const flags = new Uint8Array(N);
          const sampleLand = (px, py) => {
            const k = (py * TEX_W + px) * 4;
            const r = data[k], g = data[k + 1], b = data[k + 2];
            if (isTopology) {
              // light = land
              return (r + g + b) / 3 > 35;
            }
            // Blue-marble heuristic: ocean is strongly blue OR very dark blueish.
            const isBluish = b > r + 10 && b > g - 4;
            const isDarkSea = (r + g + b) < 55 && b >= r;
            return !isBluish && !isDarkSea;
          };
          for (let i = 0; i < N; i++) {
            const p = points[i];
            const u = (p.lng + Math.PI) / (2 * Math.PI);
            const v = (Math.PI / 2 - p.lat) / Math.PI;
            const px = Math.min(TEX_W - 1, Math.max(0, Math.floor(u * TEX_W)));
            const py = Math.min(TEX_H - 1, Math.max(0, Math.floor(v * TEX_H)));
            // Sample 3x3 neighbourhood, majority decides.
            let hits = 0, tot = 0;
            for (let dy = -1; dy <= 1; dy++) {
              const ry = py + dy;
              if (ry < 0 || ry > TEX_H - 1) continue;
              for (let dx = -1; dx <= 1; dx++) {
                const rx = (px + dx + TEX_W) % TEX_W;
                tot++;
                if (sampleLand(rx, ry)) hits++;
              }
            }
            flags[i] = hits * 2 >= tot ? 1 : 0;
          }
          landFlags = flags;
        } catch (e) {
          procFallback();
        }
      },
      () => procFallback()
    );

    function procFallback() {
      const flags = new Uint8Array(N);
      for (let i = 0; i < N; i++) {
        const p = points[i];
        const n =
          Math.sin(p.x * 3.1 + 1.2) * Math.cos(p.y * 4.2) +
          Math.cos(p.z * 3.8 - 0.5) * Math.sin(p.x * 2.1 + p.y * 1.7) +
          Math.sin(p.y * 5.3 + p.z * 2.1);
        flags[i] = n > 0.55 ? 1 : 0;
      }
      landFlags = flags;
    }

    /* ---------- Particles ---------- */
    const sparks = [];
    for (let i = 0; i < 140; i++) {
      sparks.push({
        x: Math.random(),
        y: Math.random(),
        r: Math.random() * 2 + 0.4,
        v: 0.04 + Math.random() * 0.22,
        ph: Math.random() * Math.PI * 2,
        hue: Math.random() < 0.55 ? "teal" : (Math.random() < 0.65 ? "gold" : "white"),
      });
    }

    /* ---------- Loop ---------- */
    const t0 = performance.now();
    function frame(now) {
      const t = (now - t0) / 1000;
      ctx.clearRect(0, 0, W, H);

      /* Background gradient — deep ink, warmer at bottom (hand light) */
      const g = ctx.createRadialGradient(W * 0.55, H * 0.78, 0, W * 0.55, H * 0.78, Math.max(W, H) * 0.8);
      g.addColorStop(0, "rgba(40, 50, 70, 0.45)");
      g.addColorStop(0.35, "rgba(20, 26, 38, 0.4)");
      g.addColorStop(0.65, "rgba(12, 16, 24, 1)");
      g.addColorStop(1, "rgba(8, 10, 16, 1)");
      ctx.fillStyle = g;
      ctx.fillRect(0, 0, W, H);

      /* Side circuit shimmer */
      ctx.save();
      ctx.globalCompositeOperation = "lighter";
      for (let i = 0; i < 36; i++) {
        const y = H * (0.32 + (i / 36) * 0.45) + Math.sin(t * 0.6 + i * 0.5) * 4;
        const alpha = 0.025 + 0.04 * Math.sin(t * 0.7 + i);
        ctx.strokeStyle = `rgba(110, 201, 214, ${alpha})`;
        ctx.lineWidth = 0.5;
        ctx.beginPath();
        ctx.moveTo(0, y);
        ctx.lineTo(W * 0.14, y);
        ctx.moveTo(W * 0.86, y);
        ctx.lineTo(W, y);
        ctx.stroke();
      }
      ctx.restore();

      /* Globe center & radius — right side, sized to fit fully inside the hero */
      const cx = W * 0.72;
      const cy = H * 0.42;   // subido para no tapar el dato "Soluciones 4" del hero
      const R = Math.min(W * 0.22, H * 0.36) * 0.75;   // 25% más pequeño (petición cliente)

      /* Inner glow disk behind globe */
      const gd = ctx.createRadialGradient(cx, cy, 0, cx, cy, R * 1.15);
      gd.addColorStop(0, "rgba(110, 201, 214, 0.10)");
      gd.addColorStop(0.5, "rgba(110, 201, 214, 0.04)");
      gd.addColorStop(1, "rgba(0,0,0,0)");
      ctx.fillStyle = gd;
      ctx.fillRect(0, 0, W, H);

      /* Rotation: slow tilt + Earth-like yaw */
      const ay = t * 0.22;
      const ax = -0.32 + Math.sin(t * 0.1) * 0.04;   // ~axial tilt
      const cosY = Math.cos(ay), sinY = Math.sin(ay);
      const cosX = Math.cos(ax), sinX = Math.sin(ax);

      const persp = 1.7;

      /* Project all points */
      const projected = [];
      for (let i = 0; i < N; i++) {
        const p = points[i];
        let x = p.x * cosY - p.z * sinY;
        let z = p.x * sinY + p.z * cosY;
        let y = p.y * cosX - z * sinX;
        z = p.y * sinX + z * cosX;
        const f = persp / (persp - z);
        const sx = cx + x * R * f;
        const sy = cy + y * R * f;
        projected.push({ sx, sy, z });
      }

      /* Draw points: only land (if mask known), with strong teal palette */
      ctx.save();
      ctx.globalCompositeOperation = "lighter";
      const noMask = !landFlags;
      for (let i = 0; i < N; i++) {
        const land = noMask ? false : !!landFlags[i];
        const p = projected[i];
        const depth = (p.z + 1) * 0.5;
        const front = p.z > 0.0;
        if (!front && !noMask) continue;

        if (land) {
          const size = 0.55 + depth * 1.1;
          const alpha = 0.7 + depth * 0.3;
          ctx.fillStyle = `rgba(110, 201, 214, ${Math.min(1, alpha)})`;
          ctx.beginPath();
          ctx.arc(p.sx, p.sy, size, 0, Math.PI * 2);
          ctx.fill();
        } else if (noMask) {
          const size = 0.4 + depth * 1.2;
          const alpha = 0.15 + depth * 0.5;
          ctx.fillStyle = `rgba(200, 220, 230, ${alpha})`;
          ctx.beginPath();
          ctx.arc(p.sx, p.sy, size, 0, Math.PI * 2);
          ctx.fill();
        } else {
          // ocean point: thin teal dust on front face to give sphere feel
          if (front && ((i + Math.floor(t * 30)) % 11 === 0)) {
            const size = 0.35 + depth * 0.7;
            ctx.fillStyle = `rgba(110, 201, 214, ${0.06 + depth * 0.10})`;
            ctx.beginPath();
            ctx.arc(p.sx, p.sy, size, 0, Math.PI * 2);
            ctx.fill();
          }
        }
      }
      ctx.restore();

      /* Latitude / longitude grid — faint teal, reinforces "sphere = Earth" */
      ctx.save();
      ctx.globalCompositeOperation = "lighter";
      ctx.strokeStyle = "rgba(110, 201, 214, 0.08)";
      ctx.lineWidth = 0.4;
      // parallels (every 30°)
      for (let latDeg = -60; latDeg <= 60; latDeg += 30) {
        const lat = (latDeg * Math.PI) / 180;
        ctx.beginPath();
        const steps = 180;
        let started = false;
        for (let s = 0; s <= steps; s++) {
          const lng = -Math.PI + (s / steps) * 2 * Math.PI;
          const px = Math.cos(lat) * Math.cos(lng);
          const py = Math.sin(lat);
          const pz = Math.cos(lat) * Math.sin(lng);
          let x = px * cosY - pz * sinY;
          let z = px * sinY + pz * cosY;
          let y = py * cosX - z * sinX;
          z = py * sinX + z * cosX;
          if (z < -0.05) { started = false; continue; }
          const f = persp / (persp - z);
          const sx = cx + x * R * f;
          const sy = cy + y * R * f;
          if (!started) { ctx.moveTo(sx, sy); started = true; }
          else ctx.lineTo(sx, sy);
        }
        ctx.stroke();
      }
      // meridians (every 30°)
      for (let lngDeg = -180; lngDeg < 180; lngDeg += 30) {
        const lng = (lngDeg * Math.PI) / 180;
        ctx.beginPath();
        const steps = 90;
        let started = false;
        for (let s = 0; s <= steps; s++) {
          const lat = -Math.PI / 2 + (s / steps) * Math.PI;
          const px = Math.cos(lat) * Math.cos(lng);
          const py = Math.sin(lat);
          const pz = Math.cos(lat) * Math.sin(lng);
          let x = px * cosY - pz * sinY;
          let z = px * sinY + pz * cosY;
          let y = py * cosX - z * sinX;
          z = py * sinX + z * cosX;
          if (z < -0.05) { started = false; continue; }
          const f = persp / (persp - z);
          const sx = cx + x * R * f;
          const sy = cy + y * R * f;
          if (!started) { ctx.moveTo(sx, sy); started = true; }
          else ctx.lineTo(sx, sy);
        }
        ctx.stroke();
      }
      ctx.restore();

      /* Subtle sphere outline */
      ctx.save();
      ctx.strokeStyle = "rgba(110, 201, 214, 0.10)";
      ctx.lineWidth = 0.8;
      ctx.beginPath();
      ctx.arc(cx, cy, R * 1.005, 0, Math.PI * 2);
      ctx.stroke();
      ctx.restore();

      /* Saturn-like ring (faint) */
      ctx.save();
      ctx.globalCompositeOperation = "lighter";
      ctx.strokeStyle = "rgba(110, 201, 214, 0.16)";
      ctx.lineWidth = 0.6;
      ctx.beginPath();
      const steps = 220;
      for (let i = 0; i <= steps; i++) {
        const a = (i / steps) * Math.PI * 2;
        const px = Math.cos(a), py = 0, pz = Math.sin(a);
        let x = px * cosY - pz * sinY;
        let z = px * sinY + pz * cosY;
        let y = py * cosX - z * sinX;
        z = py * sinX + z * cosX;
        const f = persp / (persp - z);
        const sx = cx + x * R * 1.18 * f;
        const sy = cy + y * R * 1.18 * f;
        if (i === 0) ctx.moveTo(sx, sy); else ctx.lineTo(sx, sy);
      }
      ctx.stroke();
      ctx.restore();

      /* Sparks */
      ctx.save();
      ctx.globalCompositeOperation = "lighter";
      sparks.forEach((s) => {
        const y = ((s.y + t * s.v * 0.05) % 1);
        const py = (1 - y) * H;
        const px = (s.x + Math.sin(t * 0.4 + s.ph) * 0.02) * W;
        const a = 0.4 + 0.6 * Math.sin(t * 2 + s.ph);
        if (s.hue === "teal") {
          ctx.fillStyle = `rgba(110, 201, 214, ${a * 0.55})`;
        } else if (s.hue === "gold") {
          ctx.fillStyle = `rgba(255, 200, 110, ${a * 0.55})`;
        } else {
          ctx.fillStyle = `rgba(220, 235, 240, ${a * 0.4})`;
        }
        ctx.beginPath();
        ctx.arc(px, py, s.r, 0, Math.PI * 2);
        ctx.fill();
      });
      ctx.restore();

      /* Warm bottom crescent (hands light suggestion) */
      const handGrad = ctx.createRadialGradient(W * 0.6, H * 1.05, 0, W * 0.6, H * 1.05, W * 0.55);
      handGrad.addColorStop(0, "rgba(220, 130, 60, 0.18)");
      handGrad.addColorStop(0.5, "rgba(120, 80, 80, 0.06)");
      handGrad.addColorStop(1, "rgba(0,0,0,0)");
      ctx.fillStyle = handGrad;
      ctx.fillRect(0, 0, W, H);

      /* Left-side text-readability vignette */
      const vg = ctx.createLinearGradient(0, 0, W, 0);
      vg.addColorStop(0, "rgba(8, 10, 16, 0.72)");
      vg.addColorStop(0.42, "rgba(8, 10, 16, 0.2)");
      vg.addColorStop(0.7, "rgba(8, 10, 16, 0)");
      ctx.fillStyle = vg;
      ctx.fillRect(0, 0, W, H);

      rafRef.current = requestAnimationFrame(frame);
    }
    rafRef.current = requestAnimationFrame(frame);

    return () => {
      cancelAnimationFrame(rafRef.current);
      ro.disconnect();
    };
  }, []);

  return (
    <div className="hero-bg" aria-hidden="true">
      <canvas ref={canvasRef} className="hero-bg__canvas" />
      <div className="hero-bg__grain" />
      <div className="hero-bg__binary hero-bg__binary--l">
        {Array.from({ length: 6 }).map((_, i) => (
          <span key={i} style={{ animationDelay: `${i * 0.5}s` }}>01010101</span>
        ))}
      </div>
      <div className="hero-bg__binary hero-bg__binary--r">
        {Array.from({ length: 6 }).map((_, i) => (
          <span key={i} style={{ animationDelay: `${i * 0.7}s` }}>01010101</span>
        ))}
      </div>
    </div>
  );
}

Object.assign(window, { HeroBackground });
