/* global React, d3, topojson */

/**
 * globe-car.jsx
 *
 * Contains two visual components used in the About section:
 *
 *  Globe  — An interactive rotating globe built with D3's geoOrthographic
 *           projection and real country geometry from the world-atlas package.
 *           Visited countries (India, UAE, USA) are highlighted in electric blue.
 *           City dot markers pause the rotation and show a label on hover.
 *
 *  F1Car  — A static side-view SVG silhouette of a Ferrari F1 car (Rosso Corsa
 *           gradient, halo, helmet, rear wing, chrome wheels). No animation —
 *           it exists purely as a visual hobby marker in the About section.
 *
 * Dependencies (loaded via CDN in index.html before this file):
 *  d3@7            — geoOrthographic, geoPath, geoDistance
 *  topojson-client — converts TopoJSON topology to GeoJSON features
 *
 * Exports: window.Globe, window.F1Car, window.PLACES
 */

const { useState: useStateG, useEffect: useEffectG, useRef: useRefG, useMemo: useMemoG } = React;

/**
 * Cities/regions Saketh has lived in or visited.
 * Each entry is projected as a dot on the globe when facing the viewer.
 */
const PLACES = [
  { name: "Hyderabad", lat: 17.4, lng: 78.5 },
  { name: "Goa", lat: 15.3, lng: 74.1 },
  { name: "Uttarakhand", lat: 30.3, lng: 78.0 },
  { name: "Tamil Nadu", lat: 11.1, lng: 78.7 },
  { name: "Tirupati", lat: 13.6, lng: 79.4 },
  { name: "Delhi", lat: 28.6, lng: 77.2 },
  { name: "Ambala", lat: 30.4, lng: 76.8 },
  { name: "Dubai", lat: 25.3, lng: 55.3 },
  { name: "Abu Dhabi", lat: 24.5, lng: 54.4 },
  { name: "Massachusetts", lat: 42.4, lng: -71.4 },
  { name: "Maine", lat: 45.3, lng: -69.0 },
  { name: "New Hampshire", lat: 43.2, lng: -71.6 },
  { name: "New York", lat: 42.2, lng: -75.0 },
  { name: "Vermont", lat: 44.0, lng: -72.7 },
  { name: "Washington DC", lat: 38.9, lng: -77.0 },
  { name: "Pennsylvania", lat: 41.2, lng: -77.2 },
  { name: "Chicago", lat: 41.9, lng: -87.6 },
  { name: "Illinois", lat: 40.0, lng: -89.0 },
  { name: "Denver", lat: 39.7, lng: -104.9 },
  { name: "Wisconsin", lat: 44.5, lng: -89.5 },
  { name: "Pittsburgh", lat: 40.4, lng: -79.9 },
];

/**
 * ISO M49 numeric codes for countries to fill with electric blue.
 * 356 = India · 784 = United Arab Emirates · 840 = United States
 * These codes match the feature.id values in the world-atlas TopoJSON.
 */
const VISITED_COUNTRIES = new Set(["356", "784", "840"]);

/**
 * Globe — rotating orthographic world map.
 *
 * @param {number} size — SVG canvas size in px (diameter, default 280)
 */
function Globe({ size = 280 }) {
  const [topology, setTopology] = useStateG(null); // world-atlas TopoJSON
  const [error, setError] = useStateG(false);      // fetch failed
  const [lambda, setLambda] = useStateG(0);        // longitudinal rotation angle
  const [hovered, setHovered] = useStateG(null);   // index of hovered city dot
  const reqRef    = useRefG(); // rAF handle
  const lastRef   = useRefG(); // previous timestamp for delta-time rotation
  const pausedRef = useRefG(false); // true while mouse is over the globe

  // Fetch the 110m-resolution world topology once on mount.
  useEffectG(() => {
    let cancelled = false;
    fetch("https://unpkg.com/world-atlas@2.0.2/countries-110m.json")
      .then((r) => r.json())
      .then((topo) => {
        if (!cancelled) setTopology(topo);
      })
      .catch(() => {
        if (!cancelled) setError(true);
      });
    return () => { cancelled = true; };
  }, []);

  // Animation loop — increments lambda by ~0.012°/ms (~1 full rotation per 30 s).
  // Delta-time approach keeps rotation speed consistent regardless of frame rate.
  useEffectG(() => {
    const animate = (t) => {
      if (lastRef.current == null) lastRef.current = t;
      const dt = t - lastRef.current;
      lastRef.current = t;
      if (!pausedRef.current) {
        setLambda((l) => (l + dt * 0.012) % 360);
      }
      reqRef.current = requestAnimationFrame(animate);
    };
    reqRef.current = requestAnimationFrame(animate);
    return () => cancelAnimationFrame(reqRef.current);
  }, []);

  const R = size / 2 - 4;
  const cx = size / 2;
  const cy = size / 2;

  const projection = useMemoG(() => {
    if (!window.d3) return null;
    return window.d3
      .geoOrthographic()
      .scale(R)
      .translate([cx, cy])
      .rotate([lambda, -15, 0])
      .clipAngle(90);
  }, [lambda, size]);

  const pathGen = useMemoG(() => {
    if (!projection || !window.d3) return null;
    return window.d3.geoPath(projection);
  }, [projection]);

  const features = useMemoG(() => {
    if (!topology || !window.topojson) return [];
    const fc = window.topojson.feature(topology, topology.objects.countries);
    return fc.features;
  }, [topology]);

  // Returns true if [lat, lng] is on the front hemisphere currently facing the viewer.
  // Uses the great-circle distance from the current projection center; points
  // beyond 90° are behind the globe and should not render.
  const isVisible = (lat, lng) => {
    if (!projection || !window.d3) return false;
    const rotate = projection.rotate();
    const center = [-rotate[0], -rotate[1]];
    const dist = window.d3.geoDistance(center, [lng, lat]);
    return dist < Math.PI / 2 - 0.02; // small margin avoids edge clipping
  };

  return (
    <div
      className="relative inline-block"
      onMouseEnter={() => (pausedRef.current = true)}
      onMouseLeave={() => {
        pausedRef.current = false;
        setHovered(null);
      }}
    >
      <svg
        width={size}
        height={size}
        viewBox={`0 0 ${size} ${size}`}
        className="select-none"
      >
        <defs>
          <radialGradient id="globe-glow" cx="50%" cy="50%" r="50%">
            <stop offset="60%" stopColor="rgba(0, 21, 255, 0)" />
            <stop offset="95%" stopColor="rgba(0, 21, 255, 0.18)" />
            <stop offset="100%" stopColor="rgba(0, 21, 255, 0)" />
          </radialGradient>
          <radialGradient id="globe-sphere" cx="36%" cy="32%" r="74%">
            <stop offset="0%" stopColor="rgba(0, 21, 255, 0.06)" />
            <stop offset="65%" stopColor="rgba(0, 21, 255, 0.025)" />
            <stop offset="100%" stopColor="rgba(0, 21, 255, 0)" />
          </radialGradient>
        </defs>

        <circle cx={cx} cy={cy} r={R + 4} fill="url(#globe-glow)" />
        <circle
          cx={cx}
          cy={cy}
          r={R}
          fill="url(#globe-sphere)"
          stroke="currentColor"
          strokeOpacity="0.22"
          strokeWidth="1"
        />

        {pathGen && features.length > 0 && (
          <g>
            {features.map((f, i) => {
              const d = pathGen(f);
              if (!d) return null;
              const visited = VISITED_COUNTRIES.has(String(f.id));
              return (
                <path
                  key={f.id ?? i}
                  d={d}
                  fill={visited ? "#0015ff" : "currentColor"}
                  fillOpacity={visited ? 0.82 : 0.07}
                  stroke={visited ? "#0015ff" : "currentColor"}
                  strokeOpacity={visited ? 1 : 0.22}
                  strokeWidth={0.45}
                />
              );
            })}
          </g>
        )}

        {pathGen && (
          <path
            d={pathGen({ type: "LineString", coordinates: [[-180, 0], [-90, 0], [0, 0], [90, 0], [180, 0]] })}
            fill="none"
            stroke="currentColor"
            strokeOpacity="0.08"
            strokeWidth="0.5"
            strokeDasharray="2 3"
          />
        )}

        {!topology && !error && (
          <g>
            {[-60, -30, 0, 30, 60].map((lat) => (
              <ellipse
                key={lat}
                cx={cx}
                cy={cy - R * Math.sin((lat * Math.PI) / 180)}
                rx={R}
                ry={Math.abs(R * Math.cos((lat * Math.PI) / 180)) * 0.15}
                fill="none"
                stroke="currentColor"
                strokeOpacity="0.12"
              />
            ))}
          </g>
        )}

        {projection &&
          PLACES.map((p, i) => {
            const [x, y] = projection([p.lng, p.lat]) || [null, null];
            const visible = x != null && isVisible(p.lat, p.lng);
            if (!visible) return null;
            const isHover = hovered === i;
            return (
              <g key={p.name} style={{ transition: "opacity 0.2s", pointerEvents: "auto" }}>
                <circle
                  cx={x}
                  cy={y}
                  r={isHover ? 6 : 4}
                  fill="#0015ff"
                  fillOpacity="0.22"
                  style={{ transition: "r 0.2s" }}
                />
                <circle
                  cx={x}
                  cy={y}
                  r={isHover ? 2.6 : 1.9}
                  fill="#f4f4ed"
                  stroke="#0015ff"
                  strokeWidth="1.2"
                  onMouseEnter={() => setHovered(i)}
                  onMouseLeave={() => setHovered(null)}
                  style={{ cursor: "pointer", transition: "r 0.2s" }}
                />
              </g>
            );
          })}
      </svg>

      <div className="absolute left-0 right-0 -bottom-4 text-center pointer-events-none">
        {hovered !== null && (
          <span className="font-mono text-[10px] tracking-[0.2em] uppercase text-electric bg-cream dark:bg-ink px-2 py-0.5">
            {PLACES[hovered].name}
          </span>
        )}
      </div>

      {error && (
        <div className="absolute inset-0 flex items-center justify-center font-mono text-[10px] tracking-[0.2em] uppercase text-ink/40 dark:text-cream/40 pointer-events-none">
          Map unavailable
        </div>
      )}
    </div>
  );
}

/**
 * F1Car — stylised Ferrari F1 silhouette (Rosso Corsa). Static — no animation.
 *
 * @param {number} width — rendered width in px (height is derived from aspect ratio)
 */
function F1Car({ width = 280 }) {
  const w = width;
  const h = Math.round(w * 0.36);
  return (
    <div
      className="relative flex items-center justify-center"
      style={{ width: w, height: h }}
    >
      <svg
        viewBox="0 0 280 90"
        width={w}
        height={w * (90 / 280)}
        fill="none"
        strokeLinejoin="round"
        strokeLinecap="round"
        className="block"
      >
        <defs>
          <linearGradient id="ferrari-red" x1="0%" y1="0%" x2="0%" y2="100%">
            <stop offset="0%" stopColor="#ff3a3a" />
            <stop offset="55%" stopColor="#d50000" />
            <stop offset="100%" stopColor="#8a0000" />
          </linearGradient>
          <linearGradient id="ferrari-shine" x1="0%" y1="0%" x2="0%" y2="100%">
            <stop offset="0%" stopColor="rgba(255,255,255,0.5)" />
            <stop offset="100%" stopColor="rgba(255,255,255,0)" />
          </linearGradient>
        </defs>

        {/* Ground shadow line */}
        <line x1="20" y1="78" x2="260" y2="78" stroke="currentColor" strokeOpacity="0.15" />

        {/* Front wing */}
        <path
          d="M8 64 L52 64 L60 56 L70 56 L66 50 L40 50 L30 54 L12 56 Z"
          fill="url(#ferrari-red)"
          stroke="#5a0000"
          strokeWidth="0.7"
        />
        {/* Nose cone */}
        <path
          d="M52 56 L100 50 L120 46 L150 44 L170 46 L180 48 L180 56 Z"
          fill="url(#ferrari-red)"
          stroke="#5a0000"
          strokeWidth="0.7"
        />
        {/* Cockpit / monocoque */}
        <path
          d="M120 44 L138 30 L170 28 L186 36 L210 38 L210 56 L180 56 L180 48 L150 44 Z"
          fill="url(#ferrari-red)"
          stroke="#5a0000"
          strokeWidth="0.7"
        />
        {/* Shine */}
        <path d="M138 30 L170 28 L186 36 L150 32 Z" fill="url(#ferrari-shine)" />
        {/* Halo */}
        <path d="M138 30 Q160 12 186 36" stroke="#1a1a1a" strokeWidth="2" fill="none" />
        {/* Sidepod */}
        <path
          d="M180 48 L220 42 L240 46 L240 60 L184 60 Z"
          fill="url(#ferrari-red)"
          stroke="#5a0000"
          strokeWidth="0.7"
        />
        {/* Airbox */}
        <path
          d="M174 30 L182 24 L196 24 L204 30 L210 38 L186 36 Z"
          fill="url(#ferrari-red)"
          stroke="#5a0000"
          strokeWidth="0.7"
        />
        {/* Rear wing */}
        <rect x="244" y="22" width="22" height="6" fill="url(#ferrari-red)" stroke="#5a0000" strokeWidth="0.6" />
        <rect x="246" y="32" width="18" height="3" fill="url(#ferrari-red)" stroke="#5a0000" strokeWidth="0.6" />
        <line x1="255" y1="28" x2="255" y2="60" stroke="#8a0000" strokeWidth="2.5" />
        {/* Diffuser */}
        <path d="M240 60 L262 58 L262 64 L240 64 Z" fill="#1a1a1a" stroke="#5a0000" strokeWidth="0.6" />
        {/* Driver helmet */}
        <path d="M156 30 Q160 22 168 22 Q176 22 178 30 Z" fill="#0a0a0a" stroke="#2a2a2a" strokeWidth="0.6" />
        {/* Yellow shield accent */}
        <rect x="194" y="42" width="4" height="4" fill="#ffd400" />
        {/* Front wheel */}
        <g transform="translate(78 62)">
          <circle cx="0" cy="0" r="16" fill="#0a0a0a" stroke="#2a2a2a" strokeWidth="1" />
          <circle cx="0" cy="0" r="9" fill="none" stroke="#888" strokeOpacity="0.7" strokeWidth="0.8" />
          <line x1="-9" y1="0" x2="9" y2="0" stroke="#bbb" strokeOpacity="0.85" />
          <line x1="0" y1="-9" x2="0" y2="9" stroke="#bbb" strokeOpacity="0.85" />
        </g>
        {/* Rear wheel */}
        <g transform="translate(228 62)">
          <circle cx="0" cy="0" r="18" fill="#0a0a0a" stroke="#2a2a2a" strokeWidth="1" />
          <circle cx="0" cy="0" r="10" fill="none" stroke="#888" strokeOpacity="0.7" strokeWidth="0.8" />
          <line x1="-10" y1="0" x2="10" y2="0" stroke="#bbb" strokeOpacity="0.85" />
          <line x1="0" y1="-10" x2="0" y2="10" stroke="#bbb" strokeOpacity="0.85" />
        </g>
      </svg>
    </div>
  );
}

// Expose to window so sections.jsx can use Globe and F1Car without imports
window.Globe = Globe;
window.F1Car = F1Car;
window.PLACES = PLACES;
