/**
 * Modern 3D DAG Visualization with bloom effects and particles.
 * Renders blockchain blocks as glowing nodes in 3D space.
 */

import { useRef, useCallback, useState, useEffect, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import ForceGraph3D from 'react-force-graph-3d';
import * as THREE from 'three';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
import type { DagVisualization } from '../lib/types';
import {
  transformToGraphData,
  getNodeColor,
  formatBlockTime,
  type GraphNode,
  type GraphLink,
  EDGE_COLORS,
} from '../lib/dagUtils';

interface DAGVisualization3DProps {
  data: DagVisualization;
  width?: number;
  height?: number;
}

interface TooltipState {
  visible: boolean;
  x: number;
  y: number;
  node: GraphNode | null;
}

export default function DAGVisualization3D({
  data,
  width = 800,
  height = 600,
}: DAGVisualization3DProps) {
  const navigate = useNavigate();
  const fgRef = useRef<any>(null);
  const containerRef = useRef<HTMLDivElement>(null);
  const [tooltip, setTooltip] = useState<TooltipState>({
    visible: false,
    x: 0,
    y: 0,
    node: null,
  });
  const [hoveredNode, setHoveredNode] = useState<string | null>(null);
  const [dimensions, setDimensions] = useState({ width, height });
  const bloomPassRef = useRef<UnrealBloomPass | null>(null);

  // Transform data for ForceGraph3D
  const graphData = useMemo(() => transformToGraphData(data), [data]);

  // Setup scene enhancements (bloom, particles, lighting)
  useEffect(() => {
    if (!fgRef.current) return;

    const fg = fgRef.current;
    const scene = fg.scene();

    // Add bloom post-processing
    if (!bloomPassRef.current) {
      const bloomPass = new UnrealBloomPass(
        new THREE.Vector2(dimensions.width, dimensions.height),
        1.5,  // strength
        0.4,  // radius
        0.85  // threshold
      );
      fg.postProcessingComposer().addPass(bloomPass);
      bloomPassRef.current = bloomPass;
    }

    // Update bloom resolution on resize
    if (bloomPassRef.current) {
      bloomPassRef.current.resolution.set(dimensions.width, dimensions.height);
    }

    // Add ambient light
    const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
    scene.add(ambientLight);

    // Add point lights for dynamic lighting
    const pointLight1 = new THREE.PointLight(0x8b5cf6, 1, 500);
    pointLight1.position.set(100, 100, 100);
    scene.add(pointLight1);

    const pointLight2 = new THREE.PointLight(0x3b82f6, 0.8, 500);
    pointLight2.position.set(-100, -100, 50);
    scene.add(pointLight2);

    // Create particle starfield
    const particleCount = 500;
    const particleGeometry = new THREE.BufferGeometry();
    const positions = new Float32Array(particleCount * 3);
    const colors = new Float32Array(particleCount * 3);

    for (let i = 0; i < particleCount; i++) {
      positions[i * 3] = (Math.random() - 0.5) * 800;
      positions[i * 3 + 1] = (Math.random() - 0.5) * 800;
      positions[i * 3 + 2] = (Math.random() - 0.5) * 800;

      // Subtle blue/purple tint
      colors[i * 3] = 0.3 + Math.random() * 0.2;
      colors[i * 3 + 1] = 0.3 + Math.random() * 0.3;
      colors[i * 3 + 2] = 0.5 + Math.random() * 0.3;
    }

    particleGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
    particleGeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));

    const particleMaterial = new THREE.PointsMaterial({
      size: 1.5,
      vertexColors: true,
      transparent: true,
      opacity: 0.6,
      blending: THREE.AdditiveBlending,
    });

    const particles = new THREE.Points(particleGeometry, particleMaterial);
    particles.name = 'starfield';
    scene.add(particles);

    // Animate particles slowly
    let animationId: number;
    const animateParticles = () => {
      particles.rotation.y += 0.0001;
      particles.rotation.x += 0.00005;
      animationId = requestAnimationFrame(animateParticles);
    };
    animateParticles();

    // Cleanup
    return () => {
      cancelAnimationFrame(animationId);
      scene.remove(ambientLight);
      scene.remove(pointLight1);
      scene.remove(pointLight2);
      scene.remove(particles);
      particleGeometry.dispose();
      particleMaterial.dispose();
    };
  }, [dimensions.width, dimensions.height]);

  // Handle container resize
  useEffect(() => {
    if (!containerRef.current) return;

    const resizeObserver = new ResizeObserver((entries) => {
      for (const entry of entries) {
        const { width: w, height: h } = entry.contentRect;
        if (w > 0 && h > 0) {
          setDimensions({ width: w, height: h });
        }
      }
    });

    resizeObserver.observe(containerRef.current);
    return () => resizeObserver.disconnect();
  }, []);

  // Handle node click - navigate to block detail
  const handleNodeClick = useCallback(
    (node: GraphNode) => {
      navigate(`/block/${node.hash}`);
    },
    [navigate]
  );

  // Handle node hover - show tooltip
  const handleNodeHover = useCallback(
    (node: GraphNode | null) => {
      setHoveredNode(node?.hash ?? null);

      if (node && containerRef.current) {
        const rect = containerRef.current.getBoundingClientRect();
        const mouseX = (window as any).__mouseX ?? rect.width / 2;
        const mouseY = (window as any).__mouseY ?? rect.height / 2;

        setTooltip({
          visible: true,
          x: mouseX,
          y: mouseY,
          node,
        });
      } else {
        setTooltip((prev) => ({ ...prev, visible: false, node: null }));
      }
    },
    []
  );

  // Track mouse position for tooltip
  useEffect(() => {
    const handleMouseMove = (e: MouseEvent) => {
      if (containerRef.current) {
        const rect = containerRef.current.getBoundingClientRect();
        (window as any).__mouseX = e.clientX - rect.left;
        (window as any).__mouseY = e.clientY - rect.top;
      }
    };

    window.addEventListener('mousemove', handleMouseMove);
    return () => window.removeEventListener('mousemove', handleMouseMove);
  }, []);

  // Custom node rendering with glow effect
  const nodeThreeObject = useCallback(
    (node: GraphNode) => {
      const isHovered = hoveredNode === node.hash;
      const color = isHovered ? '#fbbf24' : getNodeColor(node);
      const size = Math.max(4, Math.sqrt(node.val || 1) * 2);

      // Create glowing sphere
      const geometry = new THREE.SphereGeometry(size, 32, 32);

      // Inner solid sphere
      const innerMaterial = new THREE.MeshPhongMaterial({
        color: new THREE.Color(color),
        emissive: new THREE.Color(color),
        emissiveIntensity: isHovered ? 0.8 : 0.4,
        shininess: 100,
        transparent: true,
        opacity: 0.95,
      });
      const innerSphere = new THREE.Mesh(geometry, innerMaterial);

      // Outer glow sphere
      const glowGeometry = new THREE.SphereGeometry(size * 1.3, 32, 32);
      const glowMaterial = new THREE.MeshBasicMaterial({
        color: new THREE.Color(color),
        transparent: true,
        opacity: isHovered ? 0.4 : 0.15,
        blending: THREE.AdditiveBlending,
      });
      const glowSphere = new THREE.Mesh(glowGeometry, glowMaterial);

      // Group both spheres
      const group = new THREE.Group();
      group.add(innerSphere);
      group.add(glowSphere);

      // Add ring for chain blocks
      if (node.isChainBlock) {
        const ringGeometry = new THREE.RingGeometry(size * 1.5, size * 1.8, 32);
        const ringMaterial = new THREE.MeshBasicMaterial({
          color: 0xfbbf24,
          transparent: true,
          opacity: 0.6,
          side: THREE.DoubleSide,
        });
        const ring = new THREE.Mesh(ringGeometry, ringMaterial);
        ring.rotation.x = Math.PI / 2;
        group.add(ring);
      }

      return group;
    },
    [hoveredNode]
  );

  // Custom link rendering with animated particles
  const linkThreeObject = useCallback((link: GraphLink) => {
    const isSelected = link.isSelectedParent;
    const color = isSelected ? EDGE_COLORS.selectedParent : EDGE_COLORS.normal;

    // Create cylinder for the link
    const material = new THREE.MeshBasicMaterial({
      color: new THREE.Color(color),
      transparent: true,
      opacity: isSelected ? 0.8 : 0.4,
    });

    const geometry = new THREE.CylinderGeometry(
      isSelected ? 0.8 : 0.4,
      isSelected ? 0.8 : 0.4,
      1,
      8
    );
    geometry.rotateX(Math.PI / 2);

    return new THREE.Mesh(geometry, material);
  }, []);

  // Update link position
  const linkPositionUpdate = useCallback(
    (obj: THREE.Object3D, { start, end }: { start: any; end: any }) => {
      if (!start || !end) return;

      const startPos = new THREE.Vector3(start.x, start.y, start.z);
      const endPos = new THREE.Vector3(end.x, end.y, end.z);

      // Position at midpoint
      obj.position.copy(startPos.clone().add(endPos).divideScalar(2));

      // Scale to match distance
      const distance = startPos.distanceTo(endPos);
      obj.scale.set(1, 1, distance);

      // Orient towards target
      obj.lookAt(endPos);
    },
    []
  );

  // Reset camera with smooth animation
  const resetCamera = useCallback(() => {
    if (fgRef.current) {
      fgRef.current.cameraPosition(
        { x: 0, y: 0, z: 350 },
        { x: 0, y: 0, z: 0 },
        1500
      );
    }
  }, []);

  // Zoom controls
  const zoomIn = useCallback(() => {
    if (fgRef.current) {
      const camera = fgRef.current.camera();
      const currentZ = camera.position.z;
      fgRef.current.cameraPosition(
        { x: camera.position.x, y: camera.position.y, z: Math.max(100, currentZ - 50) },
        null,
        500
      );
    }
  }, []);

  const zoomOut = useCallback(() => {
    if (fgRef.current) {
      const camera = fgRef.current.camera();
      const currentZ = camera.position.z;
      fgRef.current.cameraPosition(
        { x: camera.position.x, y: camera.position.y, z: Math.min(600, currentZ + 50) },
        null,
        500
      );
    }
  }, []);

  return (
    <div
      ref={containerRef}
      className="relative w-full h-[600px] rounded-lg overflow-hidden"
      style={{ background: 'radial-gradient(ellipse at center, #1a1a2e 0%, #0f0f1a 50%, #050508 100%)' }}
    >
      <ForceGraph3D
        ref={fgRef}
        graphData={graphData}
        width={dimensions.width}
        height={dimensions.height}
        backgroundColor="rgba(0,0,0,0)"
        // Node configuration
        nodeId="id"
        nodeLabel=""
        nodeThreeObject={nodeThreeObject}
        nodeThreeObjectExtend={false}
        // Link configuration
        linkSource="source"
        linkTarget="target"
        linkThreeObject={linkThreeObject}
        linkPositionUpdate={linkPositionUpdate}
        linkDirectionalParticles={2}
        linkDirectionalParticleSpeed={0.005}
        linkDirectionalParticleWidth={1.5}
        linkDirectionalParticleColor={(link: GraphLink) =>
          link.isSelectedParent ? '#fbbf24' : '#6b7280'
        }
        // DAG layout mode
        dagMode="td"
        dagLevelDistance={60}
        // Force simulation
        d3AlphaDecay={0.04}
        d3VelocityDecay={0.25}
        warmupTicks={100}
        cooldownTicks={150}
        // Interaction
        enableNodeDrag={false}
        onNodeClick={handleNodeClick}
        onNodeHover={handleNodeHover}
      />

      {/* Glassmorphism Tooltip */}
      {tooltip.visible && tooltip.node && (
        <div
          className="absolute z-20 pointer-events-none animate-in fade-in-0 zoom-in-95 duration-150"
          style={{
            left: Math.min(tooltip.x + 20, dimensions.width - 240),
            top: Math.min(tooltip.y + 20, dimensions.height - 140),
          }}
        >
          <div className="relative backdrop-blur-xl bg-black/40 border border-white/10 rounded-xl shadow-2xl p-4 min-w-[220px]">
            {/* Gradient border effect */}
            <div className="absolute inset-0 rounded-xl bg-gradient-to-br from-violet-500/20 via-transparent to-blue-500/20 pointer-events-none" />

            <div className="relative space-y-2">
              {/* Header */}
              <div className="flex items-center gap-3">
                <div
                  className="w-4 h-4 rounded-full ring-2 ring-white/20"
                  style={{
                    backgroundColor: getNodeColor(tooltip.node),
                    boxShadow: `0 0 12px ${getNodeColor(tooltip.node)}80`
                  }}
                />
                <span className="font-mono text-sm font-semibold text-white tracking-wide">
                  {tooltip.node.shortHash}
                </span>
                {tooltip.node.isChainBlock && (
                  <span className="px-1.5 py-0.5 text-[10px] font-medium bg-amber-500/20 text-amber-300 rounded border border-amber-500/30">
                    CHAIN
                  </span>
                )}
              </div>

              {/* Stats */}
              <div className="grid grid-cols-2 gap-2 text-xs">
                <div className="space-y-0.5">
                  <div className="text-gray-500 uppercase tracking-wider text-[10px]">Blue Score</div>
                  <div className="text-white font-medium">{tooltip.node.blueScore.toLocaleString()}</div>
                </div>
                <div className="space-y-0.5">
                  <div className="text-gray-500 uppercase tracking-wider text-[10px]">Transactions</div>
                  <div className="text-white font-medium">{tooltip.node.txCount}</div>
                </div>
              </div>

              {/* Timestamp */}
              <div className="pt-1 border-t border-white/5">
                <div className="text-[10px] text-gray-500">{formatBlockTime(tooltip.node.timestamp)}</div>
              </div>

              {/* Action hint */}
              <div className="flex items-center gap-1 pt-1 text-[10px] text-gray-500">
                <svg className="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 15l-2 5L9 9l11 4-5 2zm0 0l5 5M7.188 2.239l.777 2.897M5.136 7.965l-2.898-.777M13.95 4.05l-2.122 2.122m-5.657 5.656l-2.12 2.122" />
                </svg>
                Click to explore block
              </div>
            </div>
          </div>
        </div>
      )}

      {/* Modern Controls */}
      <div className="absolute top-4 right-4 flex flex-col gap-2">
        <button
          onClick={zoomIn}
          className="group p-2.5 bg-white/5 hover:bg-white/10 border border-white/10 hover:border-white/20 text-white/70 hover:text-white rounded-xl backdrop-blur-sm transition-all duration-200"
          title="Zoom in"
        >
          <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v6m3-3H7" />
          </svg>
        </button>
        <button
          onClick={zoomOut}
          className="group p-2.5 bg-white/5 hover:bg-white/10 border border-white/10 hover:border-white/20 text-white/70 hover:text-white rounded-xl backdrop-blur-sm transition-all duration-200"
          title="Zoom out"
        >
          <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM7 10h6" />
          </svg>
        </button>
        <div className="w-full h-px bg-white/10 my-1" />
        <button
          onClick={resetCamera}
          className="group p-2.5 bg-white/5 hover:bg-white/10 border border-white/10 hover:border-white/20 text-white/70 hover:text-white rounded-xl backdrop-blur-sm transition-all duration-200"
          title="Reset view"
        >
          <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
          </svg>
        </button>
      </div>

      {/* Instructions */}
      <div className="absolute bottom-4 left-4 right-4 flex justify-between items-center">
        <div className="flex items-center gap-4 text-[11px] text-white/40">
          <span className="flex items-center gap-1.5">
            <kbd className="px-1.5 py-0.5 bg-white/10 rounded text-[10px]">Drag</kbd>
            Rotate
          </span>
          <span className="flex items-center gap-1.5">
            <kbd className="px-1.5 py-0.5 bg-white/10 rounded text-[10px]">Scroll</kbd>
            Zoom
          </span>
          <span className="flex items-center gap-1.5">
            <kbd className="px-1.5 py-0.5 bg-white/10 rounded text-[10px]">Click</kbd>
            Select
          </span>
        </div>
        <div className="text-[10px] text-white/30 font-mono">
          WebGL Accelerated
        </div>
      </div>

      {/* Vignette overlay */}
      <div
        className="absolute inset-0 pointer-events-none"
        style={{
          background: 'radial-gradient(ellipse at center, transparent 40%, rgba(0,0,0,0.4) 100%)'
        }}
      />
    </div>
  );
}
