/* ============================================================
   Motion Graphics Layer for GFA Persona Matrix Lab
   - Canvas-driven perspective grid + flow particles
   - WireframeCube (header brand)
   - Matrix3D — extruded heatmap in isometric/perspective view
   - useCountUp hook — animated number tickers
   ============================================================ */

const { useEffect: useMGEffect, useRef: useMGRef, useState: useMGState, useMemo: useMGMemo } = React;

// === 1) Perspective grid + flow background ============================

function MotionBackdrop({ intensity = 1, motion = true }) {
  const ref = useMGRef(null);
  useMGEffect(() => {
    const cv = ref.current;
    if (!cv) return;
    const ctx = cv.getContext('2d');
    let raf, t0 = performance.now();

    function resize() {
      const dpr = Math.min(2, window.devicePixelRatio || 1);
      cv.width = cv.clientWidth * dpr;
      cv.height = cv.clientHeight * dpr;
      ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    }
    resize();
    window.addEventListener('resize', resize);

    // Generate flow particles (depth z, x, y) drifting toward camera
    const N = Math.floor(70 * intensity);
    const particles = Array.from({ length: N }, () => ({
      x: (Math.random() - 0.5) * 2,
      y: (Math.random() - 0.5) * 2,
      z: Math.random() * 4 + 0.2,
      sp: 0.18 + Math.random() * 0.6,
    }));

    function project(x, y, z, w, h) {
      // simple perspective: camera at z=0 looking +z
      const f = 1.2;
      const sx = w/2 + (x * f / z) * w/2;
      const sy = h/2 + (y * f / z) * h/2;
      return [sx, sy, 1 / z];
    }

    function frame(now) {
      const dt = Math.min(0.05, (now - t0) / 1000);
      t0 = now;
      const w = cv.clientWidth, h = cv.clientHeight;
      // bg — softer + animated tint
      const tintShift = (Math.sin(now/4000) + 1) * 0.5;
      ctx.fillStyle = `oklch(${0.06 + tintShift*0.02} 0.015 ${70 + tintShift*30} / 0.72)`;
      ctx.fillRect(0, 0, w, h);

      // Floor grid (perspective): vertical lines toward vanishing point
      ctx.lineWidth = 1;
      const cx = w/2, cy = h * 0.55;
      const vanishY = cy + 0; // horizon
      // verticals
      const lineCount = 22;
      for (let i = -lineCount; i <= lineCount; i++) {
        const xOff = i / lineCount;
        // parametric: from (cx + xOff*w, h) toward (cx, vanishY)
        const x0 = cx + xOff * w * 0.9;
        const y0 = h;
        const x1 = cx + xOff * w * 0.0;
        const y1 = vanishY;
        const alpha = 0.05 + (1 - Math.abs(xOff)) * 0.07;
        ctx.strokeStyle = `oklch(0.78 0.12 220 / ${alpha * intensity})`;
        ctx.beginPath();
        ctx.moveTo(x0, y0);
        ctx.lineTo(x1, y1);
        ctx.stroke();
      }
      // horizontals (scrolling)
      const off = motion ? ((now / 1500) % 1) : 0;
      for (let i = 0; i < 14; i++) {
        const t = (i + off) / 14;
        const y = vanishY + Math.pow(t, 1.5) * (h - vanishY);
        const widthRatio = Math.pow(t, 1.5);
        const x0 = cx - widthRatio * w * 0.9;
        const x1 = cx + widthRatio * w * 0.9;
        const alpha = 0.04 + t * 0.06;
        ctx.strokeStyle = `oklch(0.78 0.12 220 / ${alpha * intensity})`;
        ctx.beginPath();
        ctx.moveTo(x0, y);
        ctx.lineTo(x1, y);
        ctx.stroke();
      }

      // Flow particles
      for (const p of particles) {
        if (motion) p.z -= dt * p.sp;
        if (p.z < 0.05) {
          p.z = 4;
          p.x = (Math.random() - 0.5) * 2;
          p.y = (Math.random() - 0.5) * 2;
        }
        const [sx, sy, inv] = project(p.x, p.y, p.z, w, h);
        const sz = Math.max(0.4, inv * 4);
        const a = Math.min(0.7, inv * 0.8);
        ctx.fillStyle = `oklch(0.80 0.14 72 / ${a * intensity})`;
        ctx.beginPath();
        ctx.arc(sx, sy, sz, 0, Math.PI * 2);
        ctx.fill();
      }

      // Scanlines pulse — horizontal scanner
      if (motion) {
        const sy = ((now / 70) % h);
        const grad = ctx.createLinearGradient(0, sy - 80, 0, sy + 80);
        grad.addColorStop(0, 'rgba(240, 180, 80, 0)');
        grad.addColorStop(0.5, `rgba(240, 180, 80, ${0.04 * intensity})`);
        grad.addColorStop(1, 'rgba(240, 180, 80, 0)');
        ctx.fillStyle = grad;
        ctx.fillRect(0, sy - 80, w, 160);
      }

      raf = requestAnimationFrame(frame);
    }
    raf = requestAnimationFrame(frame);
    return () => { cancelAnimationFrame(raf); window.removeEventListener('resize', resize); };
  }, [intensity, motion]);

  return (
    <canvas
      ref={ref}
      className="motion-backdrop"
      style={{
        position: 'fixed',
        inset: 0,
        width: '100vw',
        height: '100vh',
        zIndex: 0,
        pointerEvents: 'none',
        opacity: motion ? 1 : 0.4,
        transition: 'opacity 0.4s',
      }}
    />
  );
}

// === 2) Wireframe rotating cube (brand mark) ===========================

function WireCube({ size = 28, color = 'oklch(0.80 0.14 72)' }) {
  const ref = useMGRef(null);
  useMGEffect(() => {
    let raf, a = 0;
    function frame() {
      a += 0.012;
      if (ref.current) {
        ref.current.style.transform = `rotateX(${28 + Math.sin(a*0.7)*8}deg) rotateY(${a * 60}deg)`;
      }
      raf = requestAnimationFrame(frame);
    }
    raf = requestAnimationFrame(frame);
    return () => cancelAnimationFrame(raf);
  }, []);

  const s = size;
  const half = s / 2;
  const faces = [
    { t: `translateZ(${half}px)` },
    { t: `translateZ(${-half}px) rotateY(180deg)` },
    { t: `rotateY(90deg) translateZ(${half}px)` },
    { t: `rotateY(-90deg) translateZ(${half}px)` },
    { t: `rotateX(90deg) translateZ(${half}px)` },
    { t: `rotateX(-90deg) translateZ(${half}px)` },
  ];
  return (
    <div style={{
      width: s, height: s,
      perspective: 200,
      perspectiveOrigin: '50% 50%',
    }}>
      <div ref={ref} style={{
        width: s, height: s,
        position: 'relative',
        transformStyle: 'preserve-3d',
        transform: 'rotateX(28deg) rotateY(20deg)',
        margin: '0 auto',
      }}>
        {faces.map((f, i) => (
          <div key={i} style={{
            position: 'absolute',
            width: s, height: s,
            border: `1px solid ${color}`,
            transform: f.t,
            background: 'rgba(255,200,80,0.04)',
          }}>
            <div style={{ position: 'absolute', inset: 4, border: `1px solid ${color}`, opacity: 0.4 }} />
          </div>
        ))}
      </div>
    </div>
  );
}

// === 3) Isometric/Perspective 3D Matrix ===============================

function Matrix3D({ matrix, rotateX = 55, rotateZ = 35, cellSize = 30, gap = 2 }) {
  const ref = useMGRef(null);
  const stateRef = useMGRef({ rX: rotateX, rZ: rotateZ, autoRotate: true });
  const [, force] = useMGState(0);

  function apply() {
    if (ref.current) {
      ref.current.style.transform = `rotateX(${stateRef.current.rX}deg) rotateZ(${stateRef.current.rZ}deg)`;
    }
  }

  useMGEffect(() => {
    let raf, t0 = performance.now();
    function frame(now) {
      const dt = (now - t0) / 1000;
      t0 = now;
      if (stateRef.current.autoRotate) {
        stateRef.current.rZ = (stateRef.current.rZ + dt * 8) % 360;
        apply();
      }
      raf = requestAnimationFrame(frame);
    }
    raf = requestAnimationFrame(frame);
    return () => cancelAnimationFrame(raf);
  }, []);

  function onPointerDown(e) {
    stateRef.current.autoRotate = false;
    force(x => x + 1);
    const start = { x: e.clientX, y: e.clientY, rX: stateRef.current.rX, rZ: stateRef.current.rZ };
    const move = (ev) => {
      const dx = ev.clientX - start.x;
      const dy = ev.clientY - start.y;
      stateRef.current.rZ = (start.rZ + dx * 0.5) % 360;
      stateRef.current.rX = Math.max(10, Math.min(85, start.rX - dy * 0.4));
      apply();
    };
    const up = () => {
      window.removeEventListener('pointermove', move);
      window.removeEventListener('pointerup', up);
    };
    window.addEventListener('pointermove', move);
    window.addEventListener('pointerup', up);
  }

  function toggleRotate() {
    stateRef.current.autoRotate = !stateRef.current.autoRotate;
    force(x => x + 1);
  }
  function reset() {
    stateRef.current.rX = 55;
    stateRef.current.rZ = 35;
    stateRef.current.autoRotate = false;
    apply();
    force(x => x + 1);
  }

  if (!matrix.rows.length) {
    return <div style={{ padding: 30, color: 'var(--text-mute)', fontSize: 11 }}>쿼리를 구성하세요</div>;
  }

  const max = Math.max(1, ...matrix.rows.flatMap(r => r.cells));
  const rows = matrix.rows.length;
  const cols = matrix.cols.length;

  const totalW = cols * (cellSize + gap);
  const totalD = rows * (cellSize + gap);

  return (
    <div style={{ position: 'relative', height: 480, userSelect: 'none' }}>
      <div className="m3d-stage"
        style={{
          width: '100%', height: '100%',
          perspective: '1400px',
          perspectiveOrigin: '50% 50%',
          display: 'grid', placeItems: 'center',
        }}>
        <div
          ref={ref}
          onPointerDown={onPointerDown}
          style={{
            position: 'relative',
            transformStyle: 'preserve-3d',
            transform: `rotateX(${stateRef.current.rX}deg) rotateZ(${stateRef.current.rZ}deg)`,
            width: 0, height: 0,
            cursor: 'grab',
          }}>
          {/* Floor plane */}
          <div style={{
            position: 'absolute',
            width: totalW + 40,
            height: totalD + 40,
            left: -(totalW + 40) / 2,
            top: -(totalD + 40) / 2,
            border: '1px solid var(--border-strong)',
            background: 'oklch(0.10 0.01 70 / 0.85)',
            backgroundImage: `
              linear-gradient(to right, var(--border) 1px, transparent 1px),
              linear-gradient(to bottom, var(--border) 1px, transparent 1px)
            `,
            backgroundSize: `${cellSize + gap}px ${cellSize + gap}px`,
            backgroundPosition: '20px 20px',
            transformStyle: 'preserve-3d',
          }} />
          {/* Cells */}
          {matrix.rows.map((row, ri) =>
            row.cells.map((v, ci) => {
              const intensity = Math.min(1, v / max);
              const h = Math.max(2, intensity * 100);
              const x = -totalW/2 + ci * (cellSize + gap) + cellSize/2;
              const y = -totalD/2 + ri * (cellSize + gap) + cellSize/2;
              return (
                <Cell3D key={`${ri}-${ci}`}
                  x={x} y={y} size={cellSize} height={h} intensity={intensity}
                  value={v} title={`${row.label} · ${matrix.cols[ci]}`}
                />
              );
            })
          )}
        </div>
      </div>

      {/* Controls */}
      <div style={{
        position: 'absolute', top: 10, left: 10,
        display: 'flex', gap: 6, alignItems: 'center',
        background: 'rgba(8,9,12,0.85)',
        backdropFilter: 'blur(6px)',
        padding: '6px 8px',
        border: '1px solid var(--border-strong)',
        fontFamily: 'var(--font-mono)',
        fontSize: 10,
      }}>
        <span className="label">3D</span>
        <button className="btn" data-size="sm" data-variant="ghost" onClick={toggleRotate}>
          {stateRef.current.autoRotate ? '⏸ 정지' : '▶ 회전'}
        </button>
        <button className="btn" data-size="sm" data-variant="ghost" onClick={reset}>
          ↺ 리셋
        </button>
        <span className="mute" style={{ fontSize: 9 }}>drag to rotate</span>
      </div>
      <div style={{
        position: 'absolute', top: 10, right: 10,
        fontFamily: 'var(--font-mono)', fontSize: 9, color: 'var(--text-mute)',
        background: 'rgba(8,9,12,0.7)', padding: '4px 8px',
      }}>
        rX {stateRef.current.rX.toFixed(0)}° rZ {stateRef.current.rZ.toFixed(0)}°
      </div>
    </div>
  );
}

function Cell3D({ x, y, size, height, intensity, value, title }) {
  const fill = `oklch(${0.30 + intensity * 0.50} ${0.04 + intensity * 0.18} ${72 - intensity * 30})`;
  const sideFill = `oklch(${0.18 + intensity * 0.30} ${0.04 + intensity * 0.14} ${72 - intensity * 30})`;
  const darkFill = `oklch(${0.12 + intensity * 0.20} ${0.03 + intensity * 0.10} ${72 - intensity * 30})`;
  const top = -height;
  return (
    <div
      title={`${title}: ${value.toLocaleString()}`}
      style={{
        position: 'absolute',
        left: x - size/2,
        top: y - size/2,
        width: size,
        height: size,
        transformStyle: 'preserve-3d',
        transition: 'transform 0.2s',
        cursor: 'pointer',
      }}
      onMouseEnter={e => e.currentTarget.style.filter = 'brightness(1.4)'}
      onMouseLeave={e => e.currentTarget.style.filter = 'brightness(1)'}
    >
      {/* Top face */}
      <div style={{
        position: 'absolute',
        width: size, height: size,
        background: fill,
        border: '1px solid rgba(0,0,0,0.4)',
        transform: `translateZ(${height}px)`,
        boxShadow: 'inset 0 0 12px rgba(255,255,255,0.06)',
      }} />
      {/* Front side */}
      <div style={{
        position: 'absolute',
        width: size, height: height,
        background: darkFill,
        border: '1px solid rgba(0,0,0,0.4)',
        transformOrigin: 'top center',
        transform: `translateY(${size}px) rotateX(-90deg)`,
        top: 0, left: 0,
      }} />
      {/* Right side */}
      <div style={{
        position: 'absolute',
        width: height, height: size,
        background: sideFill,
        border: '1px solid rgba(0,0,0,0.4)',
        transformOrigin: 'left center',
        transform: `translateX(${size}px) rotateY(90deg)`,
        top: 0, left: 0,
      }} />
    </div>
  );
}

// === 4) useCountUp hook ===============================================

function useCountUp(value, duration = 600) {
  const [display, setDisplay] = useMGState(value);
  const prevRef = useMGRef(value);
  useMGEffect(() => {
    const start = prevRef.current;
    const end = value;
    if (start === end) return;
    const t0 = performance.now();
    let raf;
    function frame(now) {
      const t = Math.min(1, (now - t0) / duration);
      const eased = 1 - Math.pow(1 - t, 3);
      setDisplay(start + (end - start) * eased);
      if (t < 1) raf = requestAnimationFrame(frame);
      else { prevRef.current = end; setDisplay(end); }
    }
    raf = requestAnimationFrame(frame);
    return () => cancelAnimationFrame(raf);
  }, [value]);
  return display;
}

// === 5) ParallaxPanel — subtle tilt on mouse ==========================

function ParallaxPanel({ children, intensity = 1, className = '', ...rest }) {
  const ref = useMGRef(null);
  function onMove(e) {
    if (!ref.current || intensity === 0) return;
    const r = ref.current.getBoundingClientRect();
    const x = (e.clientX - r.left) / r.width - 0.5;
    const y = (e.clientY - r.top) / r.height - 0.5;
    ref.current.style.transform = `perspective(1200px) rotateY(${x * 3 * intensity}deg) rotateX(${-y * 2 * intensity}deg) translateZ(${4 * intensity}px)`;
  }
  function onLeave() {
    if (ref.current) ref.current.style.transform = 'perspective(1200px) rotateY(0deg) rotateX(0deg) translateZ(0px)';
  }
  return (
    <div ref={ref} onMouseMove={onMove} onMouseLeave={onLeave}
      className={className}
      style={{ transition: 'transform 0.25s cubic-bezier(.2,.7,.2,1)', transformStyle: 'preserve-3d', ...rest.style }}
      {...rest}>
      {children}
    </div>
  );
}

Object.assign(window, { MotionBackdrop, WireCube, Matrix3D, useCountUp, ParallaxPanel });
