/* global React, THREE */
const { useEffect, useRef, useState } = React;

/* ============================================================================
   Atmosphere — global cinematic layer.

   Responsibilities:
   - Cursor: dot + magnetic ring, hides native cursor.
   - GlobalBlob: full-viewport fixed three.js icosahedron, breathing + distortion,
     parallax with cursor, color shift driven by which section is active.
   - HeroBackdrop / VisionBackdrop / etc. are kept inside their sections.
   ============================================================================ */

/* ----------------------------- CURSOR --------------------------------------- */
function Cursor() {
  const dotRef = useRef(null);
  const ringRef = useRef(null);
  const [label, setLabel] = useState("");

  useEffect(() => {
    document.body.classList.add("cursor-on");
    let mx = window.innerWidth / 2, my = window.innerHeight / 2;
    let rx = mx, ry = my;          // ring (lagging)
    let dx = mx, dy = my;          // dot (snappy)
    let hovering = false;
    let pressed = false;

    const onMove = (e) => {
      mx = e.clientX; my = e.clientY;
      dx = mx; dy = my;
      // Detect hoverable targets for magnetic snap + label
      const el = document.elementFromPoint(mx, my);
      const hov = el && el.closest("[data-cursor]");
      if (hov) {
        hovering = true;
        const target = hov.getAttribute("data-cursor") || "";
        setLabel(target === "true" ? "" : target);
        // Magnetic dot snap to hovered element's center
        const r = hov.getBoundingClientRect();
        const cx = r.left + r.width / 2;
        const cy = r.top + r.height / 2;
        dx = mx + (cx - mx) * 0.15;
        dy = my + (cy - my) * 0.15;
      } else {
        hovering = false;
        setLabel("");
      }
      ringRef.current && (ringRef.current.dataset.hover = hovering ? "1" : "0");
    };
    const onDown = () => { pressed = true; if (ringRef.current) ringRef.current.dataset.press = "1"; };
    const onUp =   () => { pressed = false; if (ringRef.current) ringRef.current.dataset.press = "0"; };

    let raf = 0;
    const tick = () => {
      rx += (mx - rx) * 0.18;
      ry += (my - ry) * 0.18;
      if (dotRef.current) {
        const scale = pressed ? 0.85 : (hovering ? 1.25 : 1);
        const rot = (mx - rx) * 0.08;
        dotRef.current.style.transform = `translate3d(${dx}px, ${dy}px, 0) rotate(${rot}deg) scale(${scale})`;
      }
      if (ringRef.current) {
        ringRef.current.style.transform = `translate3d(${rx}px, ${ry}px, 0) translate(-50%,-50%) scale(${hovering ? 1 : 0.6})`;
      }
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);

    window.addEventListener("mousemove", onMove, { passive: true });
    window.addEventListener("mousedown", onDown);
    window.addEventListener("mouseup", onUp);
    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("mousedown", onDown);
      window.removeEventListener("mouseup", onUp);
      document.body.classList.remove("cursor-on");
    };
  }, []);

  return (
    <React.Fragment>
      {/* 927 mark as the cursor — snaps to interactive targets */}
      <div
        id="cursor-dot"
        ref={dotRef}
        style={{
          position: "fixed", top: 0, left: 0, zIndex: 99999,
          width: 28, height: 32,
          pointerEvents: "none",
          marginLeft: -14, marginTop: -16,
          filter: "drop-shadow(0 0 6px rgba(200,204,210,0.55))",
        }}
      >
        <img
          src="assets/927-logo-white.png"
          alt=""
          style={{
            width: "100%", height: "100%",
            objectFit: "contain",
            display: "block",
          }}
        />
      </div>
      {/* Magnetic ring (hidden by default, shows over hoverables with the label) */}
      <div
        id="cursor-ring"
        ref={ringRef}
        style={{
          position: "fixed", top: 0, left: 0, zIndex: 99998,
          width: 64, height: 64, borderRadius: "50%",
          border: "1px solid rgba(200,48,47,0.7)",
          pointerEvents: "none",
          mixBlendMode: "screen",
          transition: "border-color 0.25s ease, opacity 0.25s ease",
          fontFamily: "var(--font-body)",
          fontSize: 9,
          letterSpacing: "0.3em",
          textTransform: "uppercase",
          color: "var(--color-accent-warm)",
          display: "flex", alignItems: "center", justifyContent: "center",
          whiteSpace: "nowrap",
          opacity: label ? 1 : 0,
        }}
      >
        <span style={{ position: "absolute", top: "-18px", whiteSpace: "nowrap" }}>{label}</span>
      </div>
    </React.Fragment>
  );
}

/* ----------------------------- GLOBAL BLOB ---------------------------------- */
/* Fixed-position three.js icosahedron that floats in the right of the viewport
   and parallaxes lightly with the cursor. Its emissive color and distortion
   amplitude are driven from a small global store keyed by which section the
   user is in. Exposed via window.__atm.setMood({ hue, amp }). */

window.__atm = window.__atm || {
  hue: 0.0,         // smoked red/chrome mood baseline
  amp: 0.55,        // distortion amplitude
  setMood(m) { Object.assign(window.__atm, m); },
};

function GlobalBlob() {
  const mountRef = useRef(null);

  useEffect(() => {
    const mount = mountRef.current;
    if (!mount || typeof THREE === "undefined") return;

    const W = () => window.innerWidth;
    const H = () => window.innerHeight;
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(36, W() / H(), 0.1, 100);
    camera.position.z = 5;

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

    const geo = new THREE.IcosahedronGeometry(1.5, 96);

    const mat = new THREE.MeshPhongMaterial({
      color: 0xc8ccd2,
      emissive: 0x2a2b2f,
      shininess: 80,
      flatShading: false,
      transparent: true,
      opacity: 0.95,
    });

    // shader injection — vertex distortion via simplex noise
    mat.onBeforeCompile = (shader) => {
      shader.uniforms.uTime  = { value: 0 };
      shader.uniforms.uAmp   = { value: 0.55 };
      shader.uniforms.uSpeed = { value: 0.5 };
      const noise = `
        vec3 mod289(vec3 x){return x-floor(x*(1.0/289.0))*289.0;}
        vec4 mod289(vec4 x){return x-floor(x*(1.0/289.0))*289.0;}
        vec4 permute(vec4 x){return mod289(((x*34.0)+1.0)*x);}
        vec4 taylorInvSqrt(vec4 r){return 1.79284291400159-0.85373472095314*r;}
        float snoise(vec3 v){
          const vec2 C=vec2(1.0/6.0,1.0/3.0);
          const vec4 D=vec4(0.0,0.5,1.0,2.0);
          vec3 i=floor(v+dot(v,C.yyy));
          vec3 x0=v-i+dot(i,C.xxx);
          vec3 g=step(x0.yzx,x0.xyz);
          vec3 l=1.0-g;
          vec3 i1=min(g.xyz,l.zxy);
          vec3 i2=max(g.xyz,l.zxy);
          vec3 x1=x0-i1+C.xxx;
          vec3 x2=x0-i2+C.yyy;
          vec3 x3=x0-D.yyy;
          i=mod289(i);
          vec4 p=permute(permute(permute(i.z+vec4(0.0,i1.z,i2.z,1.0))+i.y+vec4(0.0,i1.y,i2.y,1.0))+i.x+vec4(0.0,i1.x,i2.x,1.0));
          float n_=0.142857142857;
          vec3 ns=n_*D.wyz-D.xzx;
          vec4 j=p-49.0*floor(p*ns.z*ns.z);
          vec4 x_=floor(j*ns.z);
          vec4 y_=floor(j-7.0*x_);
          vec4 x=x_*ns.x+ns.yyyy;
          vec4 y=y_*ns.x+ns.yyyy;
          vec4 h=1.0-abs(x)-abs(y);
          vec4 b0=vec4(x.xy,y.xy);
          vec4 b1=vec4(x.zw,y.zw);
          vec4 s0=floor(b0)*2.0+1.0;
          vec4 s1=floor(b1)*2.0+1.0;
          vec4 sh=-step(h,vec4(0.0));
          vec4 a0=b0.xzyw+s0.xzyw*sh.xxyy;
          vec4 a1=b1.xzyw+s1.xzyw*sh.zzww;
          vec3 p0=vec3(a0.xy,h.x);
          vec3 p1=vec3(a0.zw,h.y);
          vec3 p2=vec3(a1.xy,h.z);
          vec3 p3=vec3(a1.zw,h.w);
          vec4 norm=taylorInvSqrt(vec4(dot(p0,p0),dot(p1,p1),dot(p2,p2),dot(p3,p3)));
          p0*=norm.x;p1*=norm.y;p2*=norm.z;p3*=norm.w;
          vec4 m=max(0.6-vec4(dot(x0,x0),dot(x1,x1),dot(x2,x2),dot(x3,x3)),0.0);
          m=m*m;
          return 42.0*dot(m*m,vec4(dot(p0,x0),dot(p1,x1),dot(p2,x2),dot(p3,x3)));
        }`;
      shader.vertexShader = shader.vertexShader
        .replace("#include <common>",
          `#include <common>\nuniform float uTime;\nuniform float uAmp;\nuniform float uSpeed;\n${noise}`)
        .replace("#include <begin_vertex>", `
          float t = uTime * uSpeed;
          float n  = snoise(position * 0.85 + vec3(t, t*0.7, t*0.6));
          float n2 = snoise(position * 1.7 + vec3(t*0.5, -t, t*0.4)) * 0.5;
          vec3 transformed = position + normal * (n + n2) * uAmp;
        `);
      mat.userData.shader = shader;
    };

    const mesh = new THREE.Mesh(geo, mat);
    scene.add(mesh);

    // Lights
    scene.add(new THREE.AmbientLight(0xffffff, 0.18));
    const chromeFill = new THREE.DirectionalLight(0xc8ccd2, 1.1);
    chromeFill.position.set(3, 1, 4); scene.add(chromeFill);
    const redKey = new THREE.PointLight(0xc8302f, 9, 14);
    redKey.position.set(-3, 2, 3); scene.add(redKey);
    const redRim = new THREE.PointLight(0x9b1c1c, 4, 10);
    redRim.position.set(3, -2, -2); scene.add(redRim);
    const coolFill = new THREE.PointLight(0x818891, 3, 14);
    coolFill.position.set(0, -3, 5); scene.add(coolFill);

    // Cursor parallax
    let cx = 0, cy = 0;
    const onMove = (e) => {
      cx = (e.clientX / window.innerWidth - 0.5) * 0.6;
      cy = (e.clientY / window.innerHeight - 0.5) * 0.6;
    };
    window.addEventListener("mousemove", onMove, { passive: true });

    // Scroll-driven Y offset
    let scrollY = 0;
    const onScroll = () => { scrollY = window.scrollY || window.pageYOffset; };
    window.addEventListener("scroll", onScroll, { passive: true });

    const clock = new THREE.Clock();
    let raf = 0;
    let curHue = 0.0, curAmp = 0.55;
    const animate = () => {
      const t = clock.getElapsedTime();
      if (mat.userData.shader) {
        const target = window.__atm.amp;
        curAmp += (target - curAmp) * 0.04;
        mat.userData.shader.uniforms.uAmp.value = curAmp;
        mat.userData.shader.uniforms.uTime.value = t;
      }
      // Hue lerp
      curHue += (window.__atm.hue - curHue) * 0.03;
      const c = new THREE.Color().setHSL(curHue, 0.65, 0.55);
      mat.color.copy(c);
      const ec = new THREE.Color().setHSL(curHue, 0.7, 0.18);
      mat.emissive.copy(ec);

      // Drift
      mesh.rotation.y = t * 0.15 + cx * 0.6;
      mesh.rotation.x = Math.sin(t * 0.12) * 0.2 + cy * 0.4;
      const s = 1 + Math.sin(t * (Math.PI * 2 / 8)) * 0.018;
      mesh.scale.setScalar(s);

      // Parallax/translate based on cursor + a touch of scroll
      mesh.position.x = cx * 0.7;
      mesh.position.y = -cy * 0.7 + Math.sin(t * 0.5) * 0.05 - Math.min(scrollY, 1500) * 0.0006;

      renderer.render(scene, camera);
      raf = requestAnimationFrame(animate);
    };
    animate();

    const onResize = () => {
      renderer.setSize(W(), H());
      camera.aspect = W() / H();
      camera.updateProjectionMatrix();
    };
    window.addEventListener("resize", onResize);

    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onResize);
      geo.dispose();
      mat.dispose();
      renderer.dispose();
      if (renderer.domElement.parentNode === mount) mount.removeChild(renderer.domElement);
    };
  }, []);

  return (
    <div
      ref={mountRef}
      aria-hidden="true"
      style={{
        position: "fixed",
        right: "-8vw",
        top: 0,
        width: "70vw",
        height: "100vh",
        opacity: 0.92,
        zIndex: 2,
        pointerEvents: "none",
        filter: "drop-shadow(0 30px 80px rgba(200,204,210,0.14))",
      }}
    />
  );
}

/* ----------------------------- INTRO OVERLAY ------------------------------- */
function IntroOverlay() {
  const [closed, setClosed] = useState(false);
  useEffect(() => {
    const t = setTimeout(() => setClosed(true), 1850);
    return () => clearTimeout(t);
  }, []);
  return (
    <div
      style={{
        position: "fixed", inset: 0, zIndex: 99000,
        background: "#0a0a0a",
        pointerEvents: closed ? "none" : "auto",
        animation: closed ? "intro-out 0.95s cubic-bezier(0.7,0,0.3,1) forwards" : "none",
        display: "flex", alignItems: "center", justifyContent: "center",
        flexDirection: "column", gap: 20,
      }}
    >
      <img
        src="assets/927-logo-white.png"
        alt=""
        style={{
          width: "min(220px, 28vw)",
          opacity: 0,
          animation: "line-blur 0.9s var(--ease-out) 0.1s forwards",
        }}
      />
      <div
        style={{
          fontFamily: "var(--font-body)",
          fontSize: 10, letterSpacing: "0.5em",
          textTransform: "uppercase",
          color: "var(--color-accent)",
          opacity: 0,
          animation: "line-blur 0.9s var(--ease-out) 0.55s forwards",
        }}
      >
        [ &nbsp;LOADING THE REAL THING&nbsp; ]
      </div>
      <div style={{
        position: "absolute", bottom: 32, left: 32,
        fontFamily: "var(--font-body)", fontSize: 10,
        letterSpacing: "0.4em", textTransform: "uppercase",
        color: "var(--color-ink-muted)",
      }}>EST. 2024</div>
      <div style={{
        position: "absolute", bottom: 32, right: 32,
        fontFamily: "var(--font-body)", fontSize: 10,
        letterSpacing: "0.4em", textTransform: "uppercase",
        color: "var(--color-ink-muted)",
      }}>FIVEM // DEV STUDIO</div>
    </div>
  );
}

Object.assign(window, { Cursor, GlobalBlob, IntroOverlay });
