import { useEffect, useRef, useState } from 'react';
import { ChevronLeft, Loader2, Wifi, WifiOff } from 'lucide-react';
import * as THREE from 'three';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass }     from 'three/examples/jsm/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';

interface Msg  { text: string; id: number; }
interface Star { x: number; y: number; sz: number; ph: number; spd: number; col: [number, number, number]; }

const STAR_PALETTE: [number, number, number][] = [
  [255, 255, 255], [255, 240, 210], [210, 220, 255], [255, 210, 210], [200, 255, 230],
];

function mkStar(W: number, H: number): Star {
  return {
    x: Math.random() * W | 0,
    y: Math.random() * H | 0,
    sz: Math.random() < 0.8 ? 1 : Math.random() < 0.6 ? 2 : 3,
    ph: Math.random() * Math.PI * 2,
    spd: Math.random() * 0.04 + 0.005,
    col: STAR_PALETTE[Math.random() * STAR_PALETTE.length | 0],
  };
}

// ── Shaders (verbatim from public/vortex/script.js) ──────────────────────────

const VERT_SHELL = `
  varying vec2 vUv;
  void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  }
`;

const FRAG_SHELL = `
  uniform float uTime;
  uniform vec2  uGridSize;
  uniform vec3  uColor;
  uniform float uPulseSpeed;
  uniform float uGap;
  uniform float uOpacity;
  varying vec2 vUv;
  float random(vec2 st) {
    return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);
  }
  void main() {
    vec2 gridUV = vUv * uGridSize;
    vec2 cellID = floor(gridUV);
    vec2 cellUV = fract(gridUV);
    float padding = uGap;
    float square = step(padding, cellUV.x) * step(padding, cellUV.y)
                 * step(cellUV.x, 1.0 - padding) * step(cellUV.y, 1.0 - padding);
    float rnd   = random(cellID);
    float pulse = 0.5 + 0.5 * sin(uTime * uPulseSpeed + rnd * 6.28);
    float alpha = square * (0.05 + 0.95 * pulse) * uOpacity;
    gl_FragColor = vec4(uColor, alpha);
  }
`;

const VERT_TRAIL = `
  varying vec2 vUv;
  void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  }
`;

const FRAG_TRAIL = `
  uniform float uTime;
  uniform vec3  uColor;
  uniform float uSpeed;
  uniform float uSpeedMult;
  uniform float uLength;
  uniform float uLengthMult;
  uniform float uOffset;
  uniform float uDirection;
  varying vec2 vUv;
  void main() {
    float finalSpeed  = uSpeed * uSpeedMult;
    float progress    = fract(vUv.x * 2.0 - (uTime * finalSpeed * uDirection) + uOffset);
    float finalLength = clamp(uLength * uLengthMult, 0.01, 0.99);
    float alpha = 0.0;
    if (uDirection > 0.0) {
      alpha = smoothstep(1.0 - finalLength, 1.0, progress);
    } else {
      alpha = 1.0 - smoothstep(0.0, finalLength, progress);
    }
    alpha = pow(alpha, 3.0);
    gl_FragColor = vec4(uColor, alpha);
  }
`;

// ── Component ─────────────────────────────────────────────────────────────────

export default function RealityOverridePage({ onBack }: { onBack: () => void }) {
  const containerRef = useRef<HTMLDivElement>(null);
  const starCanvasRef = useRef<HTMLCanvasElement>(null);
  const starsRef      = useRef<Star[]>([]);
  const wsRef        = useRef<WebSocket | null>(null);
  const msgIdRef     = useRef(0);
  const fadeTimer    = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
  const pausedRef    = useRef(localStorage.getItem('ro-bg') === 'off');
  const lastTapRef   = useRef(0);

  const [wsStatus,   setWsStatus]   = useState<'connecting' | 'connected' | 'disconnected'>('connecting');
  const [currentCmd, setCurrentCmd] = useState<Msg | null>(null);
  const [history,    setHistory]    = useState<Msg[]>([]);
  const [bgVisible,  setBgVisible]  = useState(() => localStorage.getItem('ro-bg') !== 'off');

  const toggleBg = () => {
    pausedRef.current = !pausedRef.current;
    const next = !pausedRef.current;
    setBgVisible(next);
    localStorage.setItem('ro-bg', next ? 'on' : 'off');
  };

  const handleTouchEnd = () => {
    const now = Date.now();
    if (now - lastTapRef.current < 300) { toggleBg(); lastTapRef.current = 0; }
    else { lastTapRef.current = now; }
  };

  // ── Three.js vortex ─────────────────────────────────────────────────────────
  useEffect(() => {
    const container = containerRef.current;
    if (!container) return;

    const W = container.clientWidth  || window.innerWidth;
    const H = container.clientHeight || window.innerHeight;

    // Scene
    const scene = new THREE.Scene();
    scene.fog = new THREE.FogExp2(0x000000, 0.12);

    // Camera
    const camera = new THREE.PerspectiveCamera(90, W / H, 0.01, 100);

    // Renderer
    const renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(W, H);
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.toneMapping = THREE.NoToneMapping;
    container.appendChild(renderer.domElement);

    // Post-processing
    const composer = new EffectComposer(renderer);
    composer.addPass(new RenderPass(scene, camera));
    const bloomPass = new UnrealBloomPass(new THREE.Vector2(W, H), 1.5, 0.4, 0.85);
    bloomPass.threshold = 0.1;
    bloomPass.strength  = 0.5;
    bloomPass.radius    = 0.6;
    composer.addPass(bloomPass);

    // Tokamak group
    const tokamakGroup = new THREE.Group();
    scene.add(tokamakGroup);

    // Grid shell
    const shellMaterial = new THREE.ShaderMaterial({
      vertexShader:   VERT_SHELL,
      fragmentShader: FRAG_SHELL,
      uniforms: {
        uTime:       { value: 0 },
        uGridSize:   { value: new THREE.Vector2(600, 300) },
        uColor:      { value: new THREE.Color('#3388ff') },
        uPulseSpeed: { value: 4.89 },
        uGap:        { value: 0.23 },
        uOpacity:    { value: 1.0 },
      },
      transparent: true,
      side: THREE.DoubleSide,
      depthWrite: false,
      blending: THREE.AdditiveBlending,
    });
    tokamakGroup.add(new THREE.Mesh(new THREE.TorusGeometry(10, 3.0, 60, 200), shellMaterial));

    // Trail lines
    const trailsGroup = new THREE.Group();
    tokamakGroup.add(trailsGroup);
    const trailMaterials: THREE.ShaderMaterial[] = [];

    for (let i = 0; i < 80; i++) {
      const laneRadius = 7.5 + Math.random() * 5.0;
      const mat = new THREE.ShaderMaterial({
        vertexShader:   VERT_TRAIL,
        fragmentShader: FRAG_TRAIL,
        uniforms: {
          uTime:       { value: 0 },
          uColor:      { value: new THREE.Color('#38cdff') },
          uSpeed:      { value: 0.1 + Math.random() * 0.3 },
          uSpeedMult:  { value: 1.0 },
          uLength:     { value: 0.1 + Math.random() * 0.2 },
          uLengthMult: { value: 0.47 },
          uOffset:     { value: Math.random() * 100.0 },
          uDirection:  { value: 1.0 },
        },
        transparent: true,
        blending: THREE.AdditiveBlending,
        depthWrite: false,
      });
      const mesh = new THREE.Mesh(new THREE.TorusGeometry(laneRadius, 0.005, 6, 120), mat);
      mesh.rotation.x = (Math.random() - 0.5) * 0.1;
      mesh.position.z = (Math.random() - 0.5) * 0.5;
      trailsGroup.add(mesh);
      trailMaterials.push(mat);
    }

    // Animation loop — automated camera flight (no OrbitControls needed)
    const clock = new THREE.Clock();
    let cameraAngle = 0;
    let animId = 0;

    const animate = () => {
      animId = requestAnimationFrame(animate);
      const delta   = clock.getDelta();
      const elapsed = clock.getElapsedTime();

      cameraAngle += 0.3 * delta;
      const r = 10;
      camera.position.set(Math.cos(cameraAngle) * r, Math.sin(cameraAngle) * r, 0.5);
      const la = cameraAngle + 0.1;
      camera.lookAt(Math.cos(la) * r, Math.sin(la) * r, 0);

      if (pausedRef.current) return;

      shellMaterial.uniforms.uTime.value = elapsed;
      trailMaterials.forEach(m => { m.uniforms.uTime.value = elapsed; });

      composer.render();
    };

    animate();

    // Resize observer keeps canvas filling its container
    const ro = new ResizeObserver(() => {
      const w = container.clientWidth;
      const h = container.clientHeight;
      camera.aspect = w / h;
      camera.updateProjectionMatrix();
      renderer.setSize(w, h);
      composer.setSize(w, h);
    });
    ro.observe(container);

    return () => {
      cancelAnimationFrame(animId);
      ro.disconnect();
      renderer.dispose();
      if (container.contains(renderer.domElement)) container.removeChild(renderer.domElement);
    };
  }, []);

  // ── Star field (always running, visible under Three.js when vortex is hidden) ─
  useEffect(() => {
    const canvas = starCanvasRef.current;
    if (!canvas) return;
    let animId = 0;
    let prevW = 0, prevH = 0;

    const loop = () => {
      const W = canvas.clientWidth, H = canvas.clientHeight;
      if (!W || !H) { animId = requestAnimationFrame(loop); return; }
      if (W !== prevW || H !== prevH) {
        canvas.width = W; canvas.height = H;
        prevW = W; prevH = H;
        starsRef.current = Array.from({ length: 200 }, () => mkStar(W, H));
      }
      const ctx = canvas.getContext('2d')!;
      ctx.fillStyle = '#000010';
      ctx.fillRect(0, 0, W, H);
      for (const st of starsRef.current) {
        st.ph += st.spd;
        const br = Math.sin(st.ph) * 0.4 + 0.6;
        const [r, g, b] = st.col;
        ctx.fillStyle = `rgba(${r},${g},${b},${br})`;
        ctx.fillRect(st.x, st.y, st.sz, st.sz);
        if (st.sz >= 3 && br > 0.85) {
          ctx.fillStyle = `rgba(${r},${g},${b},${br * 0.35})`;
          ctx.fillRect(st.x - 2, st.y + 1, st.sz + 4, 1);
          ctx.fillRect(st.x + 1, st.y - 2, 1, st.sz + 4);
        }
      }
      animId = requestAnimationFrame(loop);
    };

    animId = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(animId);
  }, []);

  // ── WebSocket ───────────────────────────────────────────────────────────────
  useEffect(() => {
    let cancelled = false;
    let ws: WebSocket | null = null;

    const connect = () => {
      if (cancelled) return;
      ws = new WebSocket(`ws://${window.location.hostname}/ws`);
      wsRef.current = ws;
      setWsStatus('connecting');
      ws.onopen    = () => { if (!cancelled) setWsStatus('connected'); };
      ws.onmessage = (e) => {
        if (cancelled) return;
        const id  = msgIdRef.current++;
        const msg: Msg = { text: String(e.data), id };
        setCurrentCmd(msg);
        setHistory(prev => [...prev.slice(-9), msg]);
        clearTimeout(fadeTimer.current);
        fadeTimer.current = setTimeout(() => setCurrentCmd(null), 5000);
      };
      ws.onclose = () => { if (!cancelled) { setWsStatus('disconnected'); setTimeout(connect, 3000); } };
      ws.onerror = () => ws?.close();
    };

    connect();
    return () => { cancelled = true; ws?.close(); };
  }, []);

  return (
    <div
      className="relative w-full h-full bg-black overflow-hidden"
      onDoubleClick={toggleBg}
      onTouchEnd={handleTouchEnd}
    >

      {/* Star field — always visible beneath the vortex */}
      <canvas ref={starCanvasRef} className="absolute inset-0 w-full h-full" style={{ imageRendering: 'pixelated' }} />

      {/* Three.js vortex — fades out on double-click/tap */}
      <div ref={containerRef} className={`absolute inset-0 transition-opacity duration-500 ${bgVisible ? 'opacity-100' : 'opacity-0'}`} />

      {/* CSS keyframe for command flash */}
      <style>{`
        @keyframes cmd-flash {
          0%   { opacity: 0; transform: translate(-50%,-50%) scale(0.92); }
          10%  { opacity: 1; transform: translate(-50%,-50%) scale(1); }
          70%  { opacity: 1; transform: translate(-50%,-50%) scale(1); }
          100% { opacity: 0; transform: translate(-50%,-50%) scale(1); }
        }
        .cmd-flash { animation: cmd-flash 5s ease-out forwards; }
      `}</style>

      {/* Back */}
      <button
        onClick={onBack}
        className="absolute top-3 left-3 z-10 flex items-center gap-1 text-white/50 hover:text-white text-xs px-2 py-1 rounded bg-black/40 border border-white/10"
      >
        <ChevronLeft className="w-3.5 h-3.5" /> Back
      </button>

      {/* WS status */}
      <div className="absolute top-3 right-3 z-10 flex items-center gap-1.5 text-xs px-2 py-1 rounded bg-black/40 border border-white/10">
        {wsStatus === 'connecting'   && <><Loader2 className="w-3.5 h-3.5 text-yellow-400 animate-spin" /><span className="text-yellow-400">Connecting…</span></>}
        {wsStatus === 'connected'    && <><Wifi    className="w-3.5 h-3.5 text-green-400" /><span className="text-green-400">Connected</span></>}
        {wsStatus === 'disconnected' && <><WifiOff className="w-3.5 h-3.5 text-red-400" /><span className="text-red-400">Reconnecting…</span></>}
      </div>

      {/* Idle title — shown only before first message */}
      {history.length === 0 && (
        <div className="absolute inset-0 flex flex-col items-center justify-center z-10 pointer-events-none select-none">
          <div
            className="text-white/20 text-xl font-mono tracking-[0.3em] uppercase"
            style={{ textShadow: '0 0 30px rgba(120,60,255,0.7)' }}
          >
            Reality Override
          </div>
          <div className="text-white/10 text-xs font-mono tracking-widest mt-2 uppercase">
            Awaiting Commands<span className="animate-pulse">_</span>
          </div>
        </div>
      )}

      {/* Centered current command */}
      {currentCmd && (
        <div
          key={currentCmd.id}
          className="cmd-flash absolute z-10 pointer-events-none select-none text-center"
          style={{ top: '50%', left: '50%', transform: 'translate(-50%,-50%)', maxWidth: '80%' }}
        >
          <div
            className="font-mono text-2xl font-bold tracking-wide text-white break-words"
            style={{ textShadow: '0 0 24px rgba(80,200,255,0.9), 0 0 60px rgba(80,200,255,0.4)' }}
          >
            {currentCmd.text}
          </div>
        </div>
      )}

      {/* History log */}
      {history.length > 0 && (
        <div className="absolute bottom-4 left-4 right-4 z-10">
          <div className="bg-black/70 border border-green-900/40 rounded px-3 py-2 space-y-0.5">
            {history.map(m => (
              <div key={m.id} className="text-green-400 text-xs font-mono truncate">
                <span className="text-green-800 mr-2">&gt;</span>{m.text}
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}
