// StratAlliance Global — homepage globe + wordmark scene.
//
// Dotted Earth with curated continent polygons: Americas (with Florida +
// Yucatan), Africa (with Horn + Cape), Asia (with India + Indochina +
// Korea + Japan), Australia, Europe, plus Madagascar, the British Isles,
// and the Indonesian arc. Land dots paint full-brand-blue; ocean dots
// stay as a faint ghost grid so the sphere's silhouette and rotation
// remain readable. Globe spins around the real 23.4° axial tilt.
//
// HQ markers pulse at Washington DC, Tokyo, and Brussels — the firm's
// actual footprint (matches the site footer).
//
// Motion phases:
//   intro (0..2.7s)  dots fade in latitude-by-latitude top-to-bottom,
//                    wordmark resolves letter-by-letter, accent rule sweeps.
//   steady (2.7s+)   continuous slow rotation + staggered HQ pulses.
//
// Used by home.jsx as <LogoScene videoSrcs={...} tintColor={...} .../>.
// `GlobeScene` is kept as a back-compat alias.

const GLOBE_BLUE = '#0E54AA';
const INK        = '#0B1426';

// ── World-space sun direction for day/night shading ─────────────────────────
// Fixed in world space (not body frame), so the lit hemisphere stays put
// while the globe rotates through it.
const SUN = (() => {
  const v = { x: 0.45, y: 0.45, z: 0.78 };
  const m = Math.hypot(v.x, v.y, v.z);
  return { x: v.x / m, y: v.y / m, z: v.z / m };
})();

// ── HQ locations ────────────────────────────────────────────────────────────
const HQ_SPECS = [
  { lat: 47.6, lon: -122.3, name: 'Seattle, WA' },
  { lat: 28.5, lon:  -81.4, name: 'Orlando, FL' },
];
const HQ = HQ_SPECS.map(h => {
  const latR = h.lat * Math.PI / 180;
  const lonR = h.lon * Math.PI / 180;
  return {
    ...h,
    v: {
      x: Math.cos(latR) * Math.sin(lonR),
      y: Math.sin(latR),
      z: Math.cos(latR) * Math.cos(lonR),
    },
  };
});

// ── Continent polygons (hand-tuned lat/lon outlines) ────────────────────────
// Curated to capture each continent's distinctive silhouette features.
const CONTINENTS = [
  // ─── Americas (one polygon, N+C+S America connected via isthmus) ──
  [
    [72,-156],[72,-115],[70,-95],[68,-78],[62,-65],
    [50,-58],[48,-65],[45,-70],[42,-72],[40,-74],
    [37,-76],[34,-77],[31,-80],[28,-80],[25,-80],
    [28,-82],[30,-85],[30,-90],[29,-94],[26,-97],
    [22,-97],[19,-95],[16,-93],
    [14,-92],[14,-89],[9,-83],[8,-79],
    [10,-75],[11,-72],
    [12,-72],[10,-62],[6,-52],[-2,-44],
    [-7,-35],[-12,-38],[-22,-42],[-30,-50],
    [-40,-62],[-50,-69],[-55,-67],
    [-50,-74],[-38,-73],[-20,-71],[-5,-81],
    [5,-78],[10,-78],
    [8,-77],[6,-78],
    [9,-80],[12,-87],[14,-92],[16,-95],[18,-100],
    [22,-105],[26,-110],[30,-115],[33,-117],
    [37,-122],[44,-124],[48,-125],[55,-131],
    [60,-141],[65,-150],[70,-160],[72,-156]
  ],
  // Greenland
  [[83,-30],[78,-15],[70,-20],[62,-42],[68,-54],[78,-55],[83,-30]],
  // Europe + W Russia
  [
    [42,-9],[44,-2],[48,-5],[50,1],[51,3],
    [55,8],[58,8],[62,5],[68,12],[71,25],
    [69,32],[69,42],[71,55],[68,65],
    [55,55],[50,55],[45,50],
    [44,40],[42,42],[42,28],[40,26],[40,22],
    [38,24],[37,28],[36,27],[36,14],
    [40,18],[38,15],[37,12],
    [40,8],[40,0],[42,-9]
  ],
  // Asia mainland
  [
    [71,55],[72,75],[72,100],[72,120],[71,140],
    [68,168],[64,180],[60,170],[55,160],
    [50,155],[46,142],[43,134],
    [40,140],[35,135],[33,131],[35,127],[39,127],
    [40,124],[35,120],[30,121],[28,120],
    [24,118],[22,114],[20,109],
    [15,108],[10,107],[8,103],
    [4,103],[2,102],[7,99],
    [14,98],[20,94],[22,91],[22,88],
    [21,86],[18,84],[15,82],[12,80],[8,78],
    [10,76],[15,73],[20,72],[23,68],
    [25,62],[25,57],
    [22,60],[18,55],[16,52],
    [12,53],[12,45],[16,43],[22,38],[30,34],
    [33,36],[36,36],[37,40],[40,46],[40,55],
    [42,60],[50,60],[55,55],[60,60],[65,65],[71,55]
  ],
  // Africa
  [
    [37,-9],[37,11],[33,22],[31,32],
    [29,33],[22,36],[16,40],[12,43],[10,46],[11,51],
    [3,42],[-1,42],[-7,40],[-15,40],[-22,36],
    [-26,33],[-30,31],[-34,26],[-35,20],[-34,18],
    [-26,15],[-22,14],[-17,12],[-12,13],
    [-6,12],[-1,9],[3,9],[4,7],[5,2],
    [4,-3],[6,-7],[10,-13],[14,-17],
    [20,-17],[24,-16],[28,-13],[33,-7],[37,-9]
  ],
  // Australia
  [[-10,113],[-13,130],[-12,135],[-12,142],[-17,146],[-25,153],[-32,151],[-38,147],[-39,143],[-35,138],[-32,132],[-32,125],[-35,118],[-22,114],[-15,123],[-10,113]],
  // Madagascar
  [[-12,49],[-15,50],[-22,48],[-25,46],[-25,44],[-22,43],[-15,46],[-12,49]],
  // British Isles
  [[60,-8],[60,1],[55,1],[51,2],[50,-3],[52,-6],[55,-7],[58,-6],[60,-8]],
  // Indonesian arc
  [[5,95],[3,98],[0,99],[-3,103],[-6,106],[-8,115],[-3,118],[0,116],[3,115],[6,118],[7,116],[5,109],[3,104],[5,98],[5,95]],
  // New Zealand
  [[-35,173],[-37,178],[-41,174],[-46,168],[-46,167],[-42,170],[-35,173]],
  // Japan
  [[44,141],[42,144],[36,141],[34,139],[33,131],[36,128],[39,134],[44,141]],
  // Philippines
  [[19,121],[17,124],[10,126],[6,123],[8,121],[14,120],[19,121]],
];

function pointInPolygon(lat, lon, poly) {
  let inside = false;
  for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
    const [latI, lonI] = poly[i];
    const [latJ, lonJ] = poly[j];
    const intersect = ((latI > lat) !== (latJ > lat)) &&
      (lon < (lonJ - lonI) * (lat - latI) / (latJ - latI + 1e-9) + lonI);
    if (intersect) inside = !inside;
  }
  return inside;
}
function isLand(lat, lon) {
  for (const c of CONTINENTS) if (pointInPolygon(lat, lon, c)) return true;
  return false;
}

// ── Dot grid ────────────────────────────────────────────────────────────────
function buildDots({ latRows = 44, equatorCount = 80, latRange = 82 } = {}) {
  const dots = [];
  const latMin = -latRange;
  const latMax =  latRange;
  for (let i = 0; i < latRows; i++) {
    const latDeg = latMin + (latMax - latMin) * (i / (latRows - 1));
    const latRad = latDeg * Math.PI / 180;
    const ring = Math.cos(latRad);
    const count = Math.max(4, Math.round(equatorCount * ring));
    const offset = (i % 2) * (180 / count);
    for (let j = 0; j < count; j++) {
      const lonDeg = -180 + offset + (j / count) * 360;
      const lon = ((lonDeg + 540) % 360) - 180;
      const land = isLand(latDeg, lon);
      dots.push({
        lat: latRad,
        lon: lon * Math.PI / 180,
        land,
        latRow: i,
      });
    }
  }
  return dots;
}
const DOTS = buildDots();
const DOT_VECS = DOTS.map(d => ({
  x: Math.cos(d.lat) * Math.sin(d.lon),
  y: Math.sin(d.lat),
  z: Math.cos(d.lat) * Math.cos(d.lon),
  land: d.land,
  latRow: d.latRow,
}));
const MAX_ROW = DOTS.reduce((m, d) => Math.max(m, d.latRow), 0);

// ── 3D helpers ──────────────────────────────────────────────────────────────
function rotY(p, a) {
  const c = Math.cos(a), s = Math.sin(a);
  return { x: p.x * c + p.z * s, y: p.y, z: -p.x * s + p.z * c };
}
function rotZ(p, a) {
  const c = Math.cos(a), s = Math.sin(a);
  return { x: p.x * c - p.y * s, y: p.x * s + p.y * c, z: p.z };
}
function transformPt(p, rotation, tilt) {
  return rotZ(rotY(p, rotation), tilt);
}

// ── Globe ───────────────────────────────────────────────────────────────────
function Globe({ cx, cy, R, rotation, tilt, introT, color = GLOBE_BLUE }) {
  function rowT(latRow) {
    const start = (latRow / (MAX_ROW + 1)) * 1.0;
    return clamp((introT - start) / 0.55, 0, 1);
  }

  const projected = [];
  for (let i = 0; i < DOT_VECS.length; i++) {
    const v = DOT_VECS[i];
    const r = transformPt(v, rotation, tilt);
    if (r.z < 0) continue;
    const rt = Easing.easeOutCubic(rowT(v.latRow));
    if (rt <= 0) continue;

    const dotSun = r.x * SUN.x + r.y * SUN.y + r.z * SUN.z;
    const sunLit = clamp(0.5 + 0.5 * dotSun, 0, 1);

    projected.push({
      px: cx + r.x * R,
      py: cy - r.y * R,
      z: r.z,
      land: v.land,
      sunLit,
      i,
      rt,
    });
  }
  projected.sort((a, b) => a.z - b.z);

  return (
    <g>
      {projected.map(({ px, py, z, land, sunLit, i, rt }) => {
        const baseSize = land ? 7.0 : 2.6;
        const rx = Math.max(baseSize * z, 0.5);
        const ry = baseSize;
        const lit = 0.6 + 0.4 * sunLit;
        const opacity = (land ? 1 : 0.55) * lit * rt;
        return (
          <ellipse
            key={i}
            cx={px}
            cy={py}
            rx={rx}
            ry={ry}
            fill={color}
            opacity={opacity}
          />
        );
      })}
    </g>
  );
}

// ── HQ markers (Seattle + Orlando) ──────────────────────────────────────────
function HQMarkers({ cx, cy, R, rotation, tilt, time, introT, color = GLOBE_BLUE }) {
  const fadeIn = Easing.easeOutCubic(clamp((introT - 1.4) / 0.6, 0, 1));
  if (fadeIn <= 0) return null;

  return (
    <g>
      {HQ.map((h, i) => {
        const r = transformPt(h.v, rotation, tilt);
        if (r.z < -0.05) return null;

        const px = cx + r.x * R;
        const py = cy - r.y * R;
        const edgeVis = clamp(r.z * 5, 0, 1);

        const pulsePeriod = 3.2;
        const phase = ((time / pulsePeriod) + i * 0.31) % 1;
        const pulseR = 6 + phase * 26;
        const pulseOp = (1 - phase) * (1 - phase) * 0.65;

        const op = fadeIn * edgeVis;
        return (
          <g key={i} opacity={op}>
            <circle
              cx={px}
              cy={py}
              r={pulseR}
              fill="none"
              stroke={color}
              strokeWidth={1.5}
              opacity={pulseOp}
            />
            <circle cx={px} cy={py} r={6.5} fill="#FFFFFF" />
            <circle cx={px} cy={py} r={4} fill={color} />
          </g>
        );
      })}
    </g>
  );
}

// ── Wordmark (letter-by-letter cascade) ─────────────────────────────────────
// No tagline — the hero is kept spare. Just STRAT / ALLIANCE / GLOBAL with
// a sweeping accent rule.
function Wordmark({ time, x, introT, color = GLOBE_BLUE }) {
  const lines = [
    { text: 'STRAT',    start: 0.55 },
    { text: 'ALLIANCE', start: 0.80 },
    { text: 'GLOBAL',   start: 1.05 },
  ];
  const charStagger = 0.04;
  const charDur = 0.35;

  function lineRiseTy(ln) {
    const t = clamp((time - ln.start) / charDur, 0, 1);
    return (1 - Easing.easeOutCubic(t)) * 26;
  }

  const ruleT = clamp((introT - 1.85) / 0.55, 0, 1);

  return (
    <g
      transform={`translate(${x}, 540)`}
      style={{ fontFamily: '"DM Serif Display", serif' }}
    >
      {lines.map((ln, i) => {
        const ty = lineRiseTy(ln);
        return (
          <text
            key={ln.text}
            x={0}
            y={(i - 1) * 150}
            textAnchor="start"
            dominantBaseline="middle"
            fontSize={146}
            fontWeight={400}
            fill={color}
            letterSpacing={2}
            fontFamily='"DM Serif Display", serif'
            transform={`translate(0, ${ty})`}
          >
            {[...ln.text].map((ch, j) => {
              const lt = clamp((time - ln.start - j * charStagger) / charDur, 0, 1);
              const eased = Easing.easeOutCubic(lt);
              return <tspan key={j} opacity={eased}>{ch}</tspan>;
            })}
          </text>
        );
      })}
      <rect x={0} y={258} width={ruleT * 760} height={1.5} fill={color} opacity={0.7} />
    </g>
  );
}

function TimestampLabeler() {
  const time = useTime();
  React.useEffect(() => {
    const root = document.querySelector('[data-video-root]');
    if (root) root.setAttribute('data-screen-label', `t=${time.toFixed(1)}s`);
  }, [Math.floor(time)]);
  return null;
}

// ── Logo scene (used by home hero) ─────────────────────────────────────────
// videoSrcs: array of video paths to play back-to-back as one continuous loop.
// tintColor / tintOpacity / tintBlur: wash applied on top of the video.
// bgMode: 'video' shows video bg; 'solid' uses tintColor as flat bg.
// logoOnDark: true paints globe + wordmark white (use on dark video bg).
function LogoScene({
  videoSrcs = [],
  tintColor = '#E8EEF7',
  tintOpacity = 0.72,
  tintBlur = 7,
  bgMode = 'video',
  logoOnDark = false,
}) {
  const list = Array.isArray(videoSrcs)
    ? videoSrcs.filter(Boolean)
    : (videoSrcs ? [videoSrcs] : []);
  const [vidIdx, setVidIdx] = React.useState(0);
  const currentSrc = list[vidIdx % Math.max(1, list.length)];
  function handleEnded() {
    if (list.length > 1) setVidIdx((i) => (i + 1) % list.length);
  }

  const time = useTime();
  const introDur = 2.7;
  const introT = Math.max(0, time / introDur);

  const period = 16; // slow, deliberate rotation
  const rotation = (time / period) * Math.PI * 2;
  const tilt = -23.4 * Math.PI / 180;

  // Centered layout: globe left, wordmark right.
  const R = 280;
  const gap = 100;
  const wordmarkWidth = 800;
  const totalWidth = R * 2 + gap + wordmarkWidth;
  const startX = (1920 - totalWidth) / 2;
  const cx = startX + R;
  const wordmarkX = cx + R + gap;

  const logoColor = logoOnDark ? '#FFFFFF' : GLOBE_BLUE;

  return (
    <div
      data-video-root
      data-screen-label="t=0.0s"
      style={{
        position: 'absolute',
        inset: 0,
        background: bgMode === 'video' ? '#000' : '#FFFFFF',
        overflow: 'hidden',
      }}
    >
      {bgMode === 'video' && currentSrc && (
        <video
          key={currentSrc}
          src={currentSrc}
          autoPlay
          muted
          loop={list.length === 1}
          playsInline
          preload="auto"
          onEnded={handleEnded}
          style={{
            position: 'absolute',
            inset: 0,
            width: '100%',
            height: '100%',
            objectFit: 'cover',
            pointerEvents: 'none',
            background: '#000',
          }}
        />
      )}
      {/* Tint wash — softens the video so the logo/wordmark stays legible. */}
      <div
        style={{
          position: 'absolute',
          inset: 0,
          background: tintColor,
          opacity: tintOpacity,
          backdropFilter: tintBlur > 0 && bgMode === 'video' ? `blur(${tintBlur}px)` : 'none',
          WebkitBackdropFilter: tintBlur > 0 && bgMode === 'video' ? `blur(${tintBlur}px)` : 'none',
          pointerEvents: 'none',
        }}
      />
      {/* Spotlight — radial gradient that calms the area behind the logo
          while letting the video stay vivid at the edges of the frame.
          Only applied in light mode; in dark mode the brand-blue wash is
          already doing the contrast work, and a white spotlight there
          would read as an unwanted glare. */}
      {bgMode === 'video' && !logoOnDark && (
        <div
          style={{
            position: 'absolute',
            inset: 0,
            background: 'radial-gradient(ellipse 62% 70% at 50% 50%, rgba(232,238,247,0.7) 0%, rgba(232,238,247,0.32) 55%, rgba(232,238,247,0) 100%)',
            pointerEvents: 'none',
          }}
        />
      )}
      <TimestampLabeler />
      <svg
        viewBox="0 0 1920 1080"
        width="100%"
        height="100%"
        style={{
          display: 'block',
          position: 'absolute',
          inset: 0,
          filter: 'drop-shadow(0 6px 28px rgba(14,84,170,0.28))',
        }}
      >
        <Globe
          cx={cx}
          cy={540}
          R={R}
          rotation={rotation}
          tilt={tilt}
          introT={introT}
          color={logoColor}
        />
        <HQMarkers
          cx={cx}
          cy={540}
          R={R}
          rotation={rotation}
          tilt={tilt}
          time={time}
          introT={introT}
          color={logoColor}
        />
        <Wordmark
          time={time}
          x={wordmarkX}
          introT={introT}
          color={logoColor}
        />
      </svg>
    </div>
  );
}

// Back-compat alias: existing home.jsx renders <GlobeScene/>.
const GlobeScene = LogoScene;

// ─────────────────────────────────────────────────────────────────────────
// Reusable tinted video background for hero sections. Same look as the
// homepage logo scene's wash, applied behind static heroes (services /
// sectors). Falls back to the tint color alone on small screens via CSS.
// ─────────────────────────────────────────────────────────────────────────
function HeroVideoBg({
  videoSrc,
  tintColor = '#E8EEF7',
  tintOpacity = 0.72,
  tintBlur = 7,
  zIndex = 0,
  mobileBreakpoint = 900,
}) {
  return (
    <>
      <style>{`
        @media (max-width: ${mobileBreakpoint - 1}px) {
          .hero-video-bg__video { display: none; }
        }
      `}</style>
      <video
        className="hero-video-bg__video"
        src={videoSrc}
        autoPlay
        muted
        loop
        playsInline
        preload="auto"
        style={{
          position: 'absolute',
          inset: 0,
          width: '100%',
          height: '100%',
          objectFit: 'cover',
          zIndex,
          pointerEvents: 'none',
          background: tintColor,
        }}
      />
      <div
        style={{
          position: 'absolute',
          inset: 0,
          background: tintColor,
          opacity: tintOpacity,
          backdropFilter: tintBlur > 0 ? `blur(${tintBlur}px)` : 'none',
          WebkitBackdropFilter: tintBlur > 0 ? `blur(${tintBlur}px)` : 'none',
          zIndex: zIndex + 1,
          pointerEvents: 'none',
        }}
      />
    </>
  );
}

// ─────────────────────────────────────────────────────────────────────────
// Shared hero theme — light vs dark wash. Used by the homepage hero
// (LogoScene) and by the services/sectors video heroes (HeroVideoBg).
// Persists across reloads via localStorage so the toggle is sticky.
// ─────────────────────────────────────────────────────────────────────────

const HERO_THEME_CONFIG = {
  light: {
    tintColor:   '#E8EEF7',
    tintOpacity: 0.72,
    tintBlur:    7,
    logoOnDark:  false,
  },
  dark: {
    // Deeper, near-ink wash gives strong contrast for white text and
    // animation strokes on top of any video. Pure brand-blue at 0.30
    // was too thin to carry text legibly on the services / sectors
    // pages where there's no logo to break up the field.
    tintColor:   '#0B1426',
    tintOpacity: 0.62,
    tintBlur:    8,
    logoOnDark:  true,
  },
};

// Returns text-stack colors for a given theme. Use these instead of
// var(--ink) on hero text so it stays legible against either wash.
function heroTextColors(theme) {
  if (theme === 'dark') {
    return {
      ink:    '#FFFFFF',
      ink2:   'rgba(255,255,255,0.92)',
      ink3:   'rgba(255,255,255,0.70)',
      line:   'rgba(255,255,255,0.30)',
      paper:  '#FFFFFF',
    };
  }
  return {
    ink:    '#0c1834',
    ink2:   '#1f2a4a',
    ink3:   '#4f5b7a',
    line:   '#d8dee9',
    paper:  '#FFFFFF',
  };
}

function useHeroTheme() {
  const [theme, setThemeState] = React.useState(() => {
    try {
      const saved = localStorage.getItem('sag-hero-theme');
      // Default to dark; only honour an explicit 'light' choice.
      return saved === 'light' ? 'light' : 'dark';
    } catch { return 'dark'; }
  });
  const setTheme = React.useCallback((t) => {
    setThemeState(t);
    try { localStorage.setItem('sag-hero-theme', t); } catch {}
  }, []);
  return [theme, setTheme];
}

// Discrete segmented theme toggle. Positioned absolutely; pass `style`
// to fine-tune placement per page.
function HeroThemeToggle({ theme, setTheme, style = {} }) {
  const dark = theme === 'dark';
  return (
    <div
      role="group"
      aria-label="Hero theme"
      style={{
        position: 'absolute',
        top: 116,
        right: 40,
        zIndex: 10,
        display: 'flex',
        gap: 0,
        padding: 3,
        background: dark ? 'rgba(255,255,255,0.10)' : 'rgba(11,20,38,0.05)',
        backdropFilter: 'blur(12px) saturate(140%)',
        WebkitBackdropFilter: 'blur(12px) saturate(140%)',
        border: `1px solid ${dark ? 'rgba(255,255,255,0.20)' : 'rgba(11,20,38,0.10)'}`,
        borderRadius: 999,
        ...style,
      }}
    >
      {['light', 'dark'].map((t) => {
        const on = theme === t;
        return (
          <button
            key={t}
            type="button"
            onClick={() => setTheme(t)}
            aria-pressed={on}
            aria-label={`${t} hero theme`}
            style={{
              all: 'unset',
              cursor: 'pointer',
              padding: '6px 14px',
              fontFamily: '"JetBrains Mono", monospace',
              fontSize: 10,
              letterSpacing: '0.22em',
              textTransform: 'uppercase',
              borderRadius: 999,
              background: on
                ? (dark ? 'rgba(255,255,255,0.20)' : 'rgba(11,20,38,0.14)')
                : 'transparent',
              color: dark
                ? (on ? '#fff' : 'rgba(255,255,255,0.7)')
                : (on ? '#0c1834' : 'rgba(11,20,38,0.55)'),
              transition: 'background 0.25s ease, color 0.25s ease',
            }}
          >
            {t === 'light' ? '☀ Light' : '☾ Dark'}
          </button>
        );
      })}
    </div>
  );
}

Object.assign(window, {
  LogoScene, GlobeScene, Globe, HQMarkers, Wordmark, TimestampLabeler,
  HeroVideoBg,
  GLOBE_BLUE, INK, CONTINENTS, DOTS, HQ, SUN,
  useHeroTheme, HeroThemeToggle, HERO_THEME_CONFIG, heroTextColors,
});
