// SpaceX-style globe: 50 cities + 5 satellites in heliosynchronous orbit, uplink arcs.
// Loaded as a Babel JSX module; depends on THREE on window.

const CITIES = [
  { name: 'New York',       lat:  40.71, lon:  -74.01 },
  { name: 'Atlanta',        lat:  33.75, lon:  -84.39 },
  { name: 'Miami',          lat:  25.76, lon:  -80.19 },
  { name: 'Chicago',        lat:  41.88, lon:  -87.63 },
  { name: 'Houston',        lat:  29.76, lon:  -95.37 },
  { name: 'Denver',         lat:  39.74, lon: -104.99 },
  { name: 'Los Angeles',    lat:  34.05, lon: -118.24 },
  { name: 'Seattle',        lat:  47.61, lon: -122.33 },
  { name: 'Honolulu',       lat:  21.31, lon: -157.86 },
  { name: 'Anchorage',      lat:  61.22, lon: -149.90 },
  { name: 'Toronto',        lat:  43.65, lon:  -79.38 },
  { name: 'Vancouver',      lat:  49.28, lon: -123.12 },
  { name: 'Mexico City',    lat:  19.43, lon:  -99.13 },
  { name: 'Bogota',         lat:   4.71, lon:  -74.07 },
  { name: 'Lima',           lat: -12.05, lon:  -77.04 },
  { name: 'Santiago',       lat: -33.45, lon:  -70.67 },
  { name: 'Buenos Aires',   lat: -34.60, lon:  -58.38 },
  { name: 'Sao Paulo',      lat: -23.55, lon:  -46.63 },
  { name: 'Rio de Janeiro', lat: -22.91, lon:  -43.17 },
  { name: 'London',         lat:  51.51, lon:   -0.13 },
  { name: 'Paris',          lat:  48.85, lon:    2.35 },
  { name: 'Madrid',         lat:  40.42, lon:   -3.70 },
  { name: 'Rome',           lat:  41.90, lon:   12.50 },
  { name: 'Berlin',         lat:  52.52, lon:   13.40 },
  { name: 'Stockholm',      lat:  59.33, lon:   18.07 },
  { name: 'Warsaw',         lat:  52.23, lon:   21.01 },
  { name: 'Athens',         lat:  37.98, lon:   23.73 },
  { name: 'Istanbul',       lat:  41.01, lon:   28.98 },
  { name: 'Moscow',         lat:  55.76, lon:   37.62 },
  { name: 'Casablanca',     lat:  33.57, lon:   -7.59 },
  { name: 'Lagos',          lat:   6.52, lon:    3.38 },
  { name: 'Cairo',          lat:  30.04, lon:   31.24 },
  { name: 'Nairobi',        lat:  -1.29, lon:   36.82 },
  { name: 'Johannesburg',   lat: -26.20, lon:   28.05 },
  { name: 'Tel Aviv',       lat:  32.07, lon:   34.78 },
  { name: 'Riyadh',         lat:  24.71, lon:   46.68 },
  { name: 'Dubai',          lat:  25.20, lon:   55.27 },
  { name: 'Karachi',        lat:  24.86, lon:   67.01 },
  { name: 'Mumbai',         lat:  19.08, lon:   72.88 },
  { name: 'Delhi',          lat:  28.61, lon:   77.21 },
  { name: 'Bangalore',      lat:  12.97, lon:   77.59 },
  { name: 'Dhaka',          lat:  23.81, lon:   90.41 },
  { name: 'Bangkok',        lat:  13.76, lon:  100.50 },
  { name: 'Singapore',      lat:   1.35, lon:  103.82 },
  { name: 'Jakarta',        lat:  -6.21, lon:  106.85 },
  { name: 'Manila',         lat:  14.60, lon:  120.98 },
  { name: 'Taipei',         lat:  25.03, lon:  121.57 },
  { name: 'Seoul',          lat:  37.57, lon:  126.98 },
  { name: 'Tokyo',          lat:  35.68, lon:  139.65 },
  { name: 'Sydney',         lat: -33.87, lon:  151.21 },
  { name: 'Auckland',       lat: -36.85, lon:  174.76 }
];

const EARTH_TEXTURE_URL = 'https://unpkg.com/three-globe/example/img/earth-blue-marble.jpg';

const NODE_COLOR  = 0x3B0764;
const NODE_GLOW   = 0x6b21a8;
const ARC_COLOR   = 0x6b21a8;
const PULSE_COLOR = 0xc4b5fd;
const ARC_SEGMENTS = 50;

function llToVec(THREE, lat, lon, r) {
  const phi = (90 - lat) * Math.PI / 180;
  const theta = -lon * Math.PI / 180;
  return new THREE.Vector3(
    r * Math.sin(phi) * Math.cos(theta),
    r * Math.cos(phi),
    r * Math.sin(phi) * Math.sin(theta)
  );
}

function makeSpacexLabel() {
  const cv = document.createElement('canvas');
  cv.width = 512; cv.height = 128;
  const ctx = cv.getContext('2d');
  ctx.fillStyle = '#fafafa';
  ctx.fillRect(0, 0, 512, 128);
  ctx.fillStyle = '#0a0a0a';
  ctx.font = 'bold 78px "Helvetica Neue", Arial, sans-serif';
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.fillText('SPACEX', 256, 64);
  ctx.fillRect(110, 105, 292, 3);
  const tex = new window.THREE.CanvasTexture(cv);
  tex.minFilter = window.THREE.LinearFilter;
  tex.magFilter = window.THREE.LinearFilter;
  return tex;
}

function createSatellite(THREE, spacexLabel, ledMaterials) {
  const g = new THREE.Group();
  const s = 0.9;
  const bw = 0.22 * s, bh = 0.075 * s, bd = 0.18 * s;

  const body = new THREE.Mesh(
    new THREE.BoxGeometry(bw, bh, bd),
    new THREE.MeshPhongMaterial({ color: 0xf2f2ee, shininess: 30 })
  );
  g.add(body);

  const labelMat = new THREE.MeshBasicMaterial({ map: spacexLabel });
  const labelTop = new THREE.Mesh(new THREE.PlaneGeometry(bw * 0.82, bd * 0.42), labelMat);
  labelTop.position.y = bh / 2 + 0.001;
  labelTop.rotation.x = -Math.PI / 2;
  g.add(labelTop);

  const labelFront = new THREE.Mesh(new THREE.PlaneGeometry(bw * 0.82, bh * 0.65), labelMat);
  labelFront.position.z = bd / 2 + 0.001;
  g.add(labelFront);

  const labelBack = new THREE.Mesh(new THREE.PlaneGeometry(bw * 0.82, bh * 0.65), labelMat);
  labelBack.position.z = -bd / 2 - 0.001;
  labelBack.rotation.y = Math.PI;
  g.add(labelBack);

  const stripeMat = new THREE.MeshBasicMaterial({ color: 0x0a0a0a });
  const stripeFront = new THREE.Mesh(new THREE.BoxGeometry(bw + 0.003, 0.012, 0.003), stripeMat);
  stripeFront.position.set(0, -bh / 2 + 0.010, bd / 2 + 0.001);
  g.add(stripeFront);
  const stripeBack = stripeFront.clone();
  stripeBack.position.z = -bd / 2 - 0.001;
  g.add(stripeBack);

  const antenna = new THREE.Mesh(
    new THREE.BoxGeometry(bw * 0.94, 0.007, bd * 0.94),
    new THREE.MeshPhongMaterial({ color: 0x0e0e0e, shininess: 60 })
  );
  antenna.position.y = -bh / 2 - 0.004;
  g.add(antenna);

  const ledMat = new THREE.MeshBasicMaterial({ color: NODE_GLOW });
  ledMaterials.push(ledMat);
  for (let i = 0; i < 5; i++) {
    const led = new THREE.Mesh(new THREE.BoxGeometry(0.012, 0.006, 0.004), ledMat);
    led.position.set(-bw / 2 + 0.025 + i * 0.035 * s, -bh / 2 + 0.018, bd / 2 + 0.0015);
    g.add(led);
  }

  const panelMat = new THREE.MeshPhongMaterial({ color: 0x12305a, shininess: 130, specular: 0x8aaaff });
  const cellMat = new THREE.MeshBasicMaterial({ color: 0x081830 });
  const armMat = new THREE.MeshPhongMaterial({ color: 0x666666 });

  const armLen = 0.07 * s, arrLen = 0.42 * s, arrW = 0.20 * s;
  [-1, 1].forEach(function (side) {
    const arm = new THREE.Mesh(new THREE.BoxGeometry(armLen, 0.010, 0.010), armMat);
    arm.position.x = side * (bw / 2 + armLen / 2);
    g.add(arm);

    const panel = new THREE.Mesh(new THREE.BoxGeometry(arrLen, 0.005, arrW), panelMat);
    panel.position.x = side * (bw / 2 + armLen + arrLen / 2);
    g.add(panel);

    for (let i = 0; i < 4; i++) {
      const ln = new THREE.Mesh(new THREE.BoxGeometry(arrLen, 0.0025, 0.003), cellMat);
      ln.position.set(side * (bw / 2 + armLen + arrLen / 2), 0.003, -arrW / 2 + (i + 1) * arrW / 5);
      g.add(ln);
    }
    for (let j = 1; j < 6; j++) {
      const ln = new THREE.Mesh(new THREE.BoxGeometry(0.0025, 0.003, arrW), cellMat);
      ln.position.set(side * (bw / 2 + armLen - arrLen / 2 + j * arrLen / 6), 0.003, 0);
      g.add(ln);
    }
  });

  return g;
}

const Globe = () => {
  const mountRef = React.useRef(null);

  React.useEffect(() => {
    const THREE = window.THREE;
    if (!THREE || !mountRef.current) return;

    const mount = mountRef.current;
    let W = mount.clientWidth || 600, H = mount.clientHeight || 600;

    const canvas = document.createElement('canvas');
    canvas.style.display = 'block';
    canvas.style.width = '100%';
    canvas.style.height = '100%';
    canvas.style.cursor = 'grab';
    mount.appendChild(canvas);

    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(40, W / H, 0.1, 100);
    camera.position.set(0, 0.5, 6.4);
    camera.lookAt(0, 0, 0);

    const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true });
    renderer.setSize(W, H);
    renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
    renderer.setClearColor(0x000000, 0);

    // Earth
    const earthGroup = new THREE.Group();
    earthGroup.rotation.z = 0.41;
    scene.add(earthGroup);

    const loader = new THREE.TextureLoader();
    const earthTex = loader.load(EARTH_TEXTURE_URL);
    earthTex.minFilter = THREE.LinearMipmapLinearFilter;
    earthTex.magFilter = THREE.LinearFilter;
    earthTex.anisotropy = 8;

    const earth = new THREE.Mesh(
      new THREE.SphereGeometry(1, 128, 128),
      new THREE.MeshPhongMaterial({ color: 0xffffff, map: earthTex, shininess: 6 })
    );
    earthGroup.add(earth);

    // City markers (hidden until active)
    const companies = CITIES.map(function (c) { return Object.assign({}, c); });
    const markerGeoCore = new THREE.SphereGeometry(0.014, 12, 12);
    const markerGeoRing = new THREE.SphereGeometry(0.024, 12, 12);
    companies.forEach(function (c) {
      const pos = llToVec(THREE, c.lat, c.lon, 1.020);
      c.localPos = pos;

      const coreMat = new THREE.MeshBasicMaterial({ color: NODE_COLOR, transparent: true, opacity: 0 });
      const core = new THREE.Mesh(markerGeoCore, coreMat);
      core.position.copy(pos);
      core.visible = false;
      earthGroup.add(core);

      const ringMat = new THREE.MeshBasicMaterial({ color: NODE_GLOW, transparent: true, opacity: 0 });
      const ring = new THREE.Mesh(markerGeoRing, ringMat);
      ring.position.copy(pos);
      ring.visible = false;
      earthGroup.add(ring);

      c.core = core; c.coreMat = coreMat;
      c.ring = ring; c.ringMat = ringMat;
      c.activeArcs = 0; c.fadeT = 0;
    });

    const spacexLabel = makeSpacexLabel();
    const ledMaterials = [];

    // Heliosynchronous constellation
    const orbits = [
      { radius: 1.42, speed: 0.0036, incl: 1.55, raan: 0.00, phase: 0.0 },
      { radius: 1.52, speed: 0.0032, incl: 1.68, raan: 1.10, phase: 1.5 },
      { radius: 1.60, speed: 0.0029, incl: 1.50, raan: 2.30, phase: 2.9 },
      { radius: 1.68, speed: 0.0027, incl: 1.72, raan: 3.55, phase: 4.3 },
      { radius: 1.76, speed: 0.0024, incl: 1.60, raan: 4.85, phase: 5.7 }
    ];
    const datacenters = [];
    orbits.forEach(function (o) {
      const raanGroup = new THREE.Group();
      raanGroup.rotation.y = o.raan;
      const orbitGroup = new THREE.Group();
      orbitGroup.rotation.x = o.incl;
      const sat = createSatellite(THREE, spacexLabel, ledMaterials);
      orbitGroup.add(sat);
      raanGroup.add(orbitGroup);
      scene.add(raanGroup);
      datacenters.push({ mesh: sat, radius: o.radius, speed: o.speed, angle: o.phase });
    });

    // Lights
    scene.add(new THREE.AmbientLight(0xffffff, 1.1));
    const sun = new THREE.DirectionalLight(0xffffff, 0.9);
    sun.position.set(5, 1.5, 4);
    scene.add(sun);
    const fill = new THREE.DirectionalLight(0xa088c4, 0.20);
    fill.position.set(-4, -1, -3);
    scene.add(fill);

    // Arcs
    const activeLinks = [];
    const pulseGeo = new THREE.SphereGeometry(0.018, 12, 12);
    const tmpStart = new THREE.Vector3();
    const tmpEnd = new THREE.Vector3();
    const tmpMid = new THREE.Vector3();

    function buildLineGeometry() {
      const geo = new THREE.BufferGeometry();
      geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array((ARC_SEGMENTS + 1) * 3), 3));
      return geo;
    }

    function spawnUplink() {
      earthGroup.updateMatrixWorld();
      const camDir = camera.position.clone().normalize();

      const visibles = companies.filter(function (c) {
        const wp = c.localPos.clone().applyMatrix4(earthGroup.matrixWorld);
        return wp.normalize().dot(camDir) > 0.18;
      });
      if (!visibles.length) return;

      const c = visibles[Math.floor(Math.random() * visibles.length)];
      const startWorld = c.localPos.clone().applyMatrix4(earthGroup.matrixWorld);

      let nearestDc = null, minD = Infinity;
      datacenters.forEach(function (dc) {
        dc.mesh.updateMatrixWorld();
        const p = new THREE.Vector3();
        dc.mesh.getWorldPosition(p);
        if (p.dot(camDir) < -0.1) return;
        const d = p.distanceTo(startWorld);
        if (d < minD) { minD = d; nearestDc = dc; }
      });
      if (!nearestDc) return;

      const geo = buildLineGeometry();
      const mat = new THREE.LineBasicMaterial({ color: ARC_COLOR, transparent: true, opacity: 0 });
      const line = new THREE.Line(geo, mat);
      scene.add(line);

      const pulseMat = new THREE.MeshBasicMaterial({ color: PULSE_COLOR, transparent: true, opacity: 1 });
      const pulse = new THREE.Mesh(pulseGeo, pulseMat);
      scene.add(pulse);

      c.activeArcs += 1;

      activeLinks.push({
        line: line, mat: mat, pulse: pulse, pulseMat: pulseMat,
        curve: new THREE.QuadraticBezierCurve3(),
        t: 0, dur: 1.7,
        direction: Math.random() < 0.7 ? 1 : -1,
        city: c, satellite: nearestDc.mesh
      });
    }

    function refreshArc(u) {
      tmpStart.copy(u.city.localPos).applyMatrix4(earthGroup.matrixWorld);
      u.satellite.getWorldPosition(tmpEnd);

      tmpMid.copy(tmpStart).add(tmpEnd).multiplyScalar(0.5);
      tmpMid.normalize().multiplyScalar(Math.max(tmpStart.length(), tmpEnd.length()) * 1.06);

      u.curve.v0.copy(tmpStart);
      u.curve.v1.copy(tmpMid);
      u.curve.v2.copy(tmpEnd);

      const arr = u.line.geometry.attributes.position.array;
      const tmp = new THREE.Vector3();
      for (let i = 0; i <= ARC_SEGMENTS; i++) {
        u.curve.getPoint(i / ARC_SEGMENTS, tmp);
        arr[i * 3]     = tmp.x;
        arr[i * 3 + 1] = tmp.y;
        arr[i * 3 + 2] = tmp.z;
      }
      u.line.geometry.attributes.position.needsUpdate = true;
      u.line.geometry.computeBoundingSphere();
    }

    // Pointer
    let dragging = false, autoRot = true;
    let camAz = 0, camEl = 0.16, camAzS = 0, camElS = 0, px = 0, py = 0;
    let autoRotTimer = null;

    const onPointerDown = (e) => {
      dragging = true; autoRot = false;
      px = e.clientX; py = e.clientY; camAzS = camAz; camElS = camEl;
      canvas.style.cursor = 'grabbing';
    };
    const onPointerMove = (e) => {
      if (!dragging) return;
      camAz = camAzS - (e.clientX - px) * 0.005;
      camEl = Math.max(-1.0, Math.min(1.0, camElS + (e.clientY - py) * 0.005));
    };
    const onPointerUp = () => {
      dragging = false;
      canvas.style.cursor = 'grab';
      if (autoRotTimer) clearTimeout(autoRotTimer);
      autoRotTimer = setTimeout(() => { autoRot = true; }, 1800);
    };
    canvas.addEventListener('pointerdown', onPointerDown);
    window.addEventListener('pointermove', onPointerMove);
    window.addEventListener('pointerup', onPointerUp);

    // Resize
    const ro = new ResizeObserver(() => {
      W = mount.clientWidth || 600; H = mount.clientHeight || 600;
      camera.aspect = W / H;
      camera.updateProjectionMatrix();
      renderer.setSize(W, H);
    });
    ro.observe(mount);

    // Render loop
    let lastSpawn = 0, frame = 0, raf = 0;
    let stopped = false;

    function tick() {
      if (stopped) return;
      raf = requestAnimationFrame(tick);
      frame++;

      const now = performance.now();
      if (now - lastSpawn > 280) { lastSpawn = now; spawnUplink(); }

      earthGroup.rotation.y += 0.0013;

      if (autoRot) camAz += 0.0006;
      const camR = 6.4;
      camera.position.x = Math.sin(camAz) * Math.cos(camEl) * camR;
      camera.position.y = Math.sin(camEl) * camR;
      camera.position.z = Math.cos(camAz) * Math.cos(camEl) * camR;
      camera.lookAt(0, 0, 0);

      datacenters.forEach(function (dc) {
        dc.angle += dc.speed;
        dc.mesh.position.set(Math.cos(dc.angle) * dc.radius, 0, Math.sin(dc.angle) * dc.radius);
        dc.mesh.rotation.y = -dc.angle - Math.PI / 2;
      });

      ledMaterials.forEach(function (m, i) {
        const p = 0.45 + 0.55 * Math.sin(frame * 0.06 + i * 0.7);
        m.color.setRGB(0.42 * p, 0.13 * p, 0.66 * p);
      });

      companies.forEach(function (c) {
        const target = c.activeArcs > 0 ? 1 : 0;
        c.fadeT += (target - c.fadeT) * 0.18;
        if (c.fadeT < 0.01) {
          c.core.visible = false; c.ring.visible = false;
        } else {
          c.core.visible = true; c.ring.visible = true;
          c.coreMat.opacity = c.fadeT;
          c.ringMat.opacity = 0.55 * c.fadeT;
          const s = 1 + (1 - c.fadeT) * 0.6;
          c.ring.scale.setScalar(s);
        }
      });

      earthGroup.updateMatrixWorld();

      for (let i = activeLinks.length - 1; i >= 0; i--) {
        const u = activeLinks[i];
        u.t += 1 / 60 / u.dur;
        if (u.t >= 1) {
          scene.remove(u.line); scene.remove(u.pulse);
          u.line.geometry.dispose(); u.mat.dispose(); u.pulseMat.dispose();
          u.city.activeArcs = Math.max(0, u.city.activeArcs - 1);
          activeLinks.splice(i, 1);
          continue;
        }
        refreshArc(u);
        let opacity;
        if (u.t < 0.18) opacity = (u.t / 0.18) * 0.85;
        else if (u.t < 0.78) opacity = 0.85;
        else opacity = (1 - u.t) / 0.22 * 0.85;
        u.mat.opacity = opacity;

        const pt = u.direction > 0 ? u.t : 1 - u.t;
        u.pulse.position.copy(u.curve.getPoint(pt));
        u.pulseMat.opacity = Math.min(1, opacity * 1.8);
      }

      renderer.render(scene, camera);
    }
    tick();

    return () => {
      stopped = true;
      cancelAnimationFrame(raf);
      if (autoRotTimer) clearTimeout(autoRotTimer);
      ro.disconnect();
      canvas.removeEventListener('pointerdown', onPointerDown);
      window.removeEventListener('pointermove', onPointerMove);
      window.removeEventListener('pointerup', onPointerUp);
      renderer.dispose();
      if (canvas.parentNode === mount) mount.removeChild(canvas);
    };
  }, []);

  return (
    <div
      className="hero-globe-stage"
      ref={mountRef}
      style={{ background: 'transparent', overflow: 'hidden', position: 'relative' }}
    >
      {!window.THREE && (
        <div className="globe-fallback">Activez WebGL pour voir l'animation</div>
      )}
    </div>
  );
};

window.Globe = Globe;
