import React, { useState, useMemo } from 'react'; import { ReverseGraphNode, ReverseGraphEdge, ReverseTraversalPath } from '../types/database'; import { calculateReverseTraversal } from '../services/relationalDataGenerator'; import { GitFork, ArrowLeft, ArrowRight, Search, Sliders, Maximize2, ShieldAlert, Layers, Eye, RotateCcw, Zap, Activity, Compass, DollarSign } from 'lucide-react'; interface ReverseNetworkGraphProps { nodes: ReverseGraphNode[]; edges: ReverseGraphEdge[]; onSelectNodeInDatabase?: (nodeId: string) => void; } // Continuous Depth Color Scale (Continuity Gradient) export const DEPTH_COLORS: Record = { 0: { bg: 'bg-rose-950/80', border: 'border-rose-500', glow: 'rgba(244, 63, 94, 0.4)', text: 'text-rose-300', hex: '#f43f5e' }, // Sink 1: { bg: 'bg-orange-950/80', border: 'border-orange-500', glow: 'rgba(249, 115, 22, 0.4)', text: 'text-orange-300', hex: '#f97316' }, // Mixer 2: { bg: 'bg-amber-950/80', border: 'border-amber-500', glow: 'rgba(234, 179, 8, 0.4)', text: 'text-amber-300', hex: '#eab308' }, // Hawala 3: { bg: 'bg-emerald-950/80', border: 'border-emerald-500', glow: 'rgba(16, 185, 129, 0.4)', text: 'text-emerald-300', hex: '#10b981' }, // Smurf 4: { bg: 'bg-cyan-950/80', border: 'border-cyan-500', glow: 'rgba(6, 182, 212, 0.4)', text: 'text-cyan-300', hex: '#06b6d4' }, // Fiat Gateway 5: { bg: 'bg-indigo-950/80', border: 'border-indigo-500', glow: 'rgba(99, 102, 241, 0.4)', text: 'text-indigo-300', hex: '#6366f1' }, // Proxy / ISP 6: { bg: 'bg-purple-950/80', border: 'border-purple-500', glow: 'rgba(168, 85, 247, 0.4)', text: 'text-purple-300', hex: '#a855f7' }, // Origin Source }; export const getDepthColor = (depth: number) => { const clamped = Math.min(Math.max(depth, 0), 6); return DEPTH_COLORS[clamped] || DEPTH_COLORS[6]; }; export const ReverseNetworkGraph: React.FC = ({ nodes, edges, onSelectNodeInDatabase, }) => { // Find default sink node (depth 0 or terminal node) const defaultSink = nodes.find(n => n.depth === 0) || nodes[nodes.length - 1]; const [selectedSinkId, setSelectedSinkId] = useState(defaultSink ? defaultSink.id : (nodes[0]?.id || '')); const [inspectedNodeId, setInspectedNodeId] = useState(null); const [traversalDirection, setTraversalDirection] = useState<'REVERSE' | 'FORWARD'>('REVERSE'); const [layoutMode, setLayoutMode] = useState<'HIERARCHICAL' | 'CONCENTRIC'>('HIERARCHICAL'); const [maxDepthFilter, setMaxDepthFilter] = useState(5); const [minAmountFilter, setMinAmountFilter] = useState(0); const [searchQuery, setSearchQuery] = useState(''); const [showAnimation, setShowAnimation] = useState(true); // Compute Active Reverse Traversal Path from selected sink const traversalResult: ReverseTraversalPath = useMemo(() => { if (!selectedSinkId || nodes.length === 0) { return { sinkId: '', paths: [], visitedNodeIds: new Set(), visitedEdgeIds: new Set(), totalUpstreamVolumeUsd: 0, maxDepth: 0, bottlenecks: [], }; } return calculateReverseTraversal(selectedSinkId, nodes, edges); }, [selectedSinkId, nodes, edges]); // Filtered nodes & edges const filteredEdges = useMemo(() => { return edges.filter(e => { const matchAmount = e.amountUsd >= minAmountFilter; const matchTraversal = traversalResult.visitedEdgeIds.has(e.id); return matchAmount && (traversalDirection === 'FORWARD' || matchTraversal || traversalResult.visitedEdgeIds.size === 0); }); }, [edges, minAmountFilter, traversalResult, traversalDirection]); // Position calculation for Hierarchical & Concentric Layouts const nodePositions = useMemo(() => { const pos: Record = {}; const width = 880; const height = 480; const paddingX = 70; const paddingY = 50; if (layoutMode === 'HIERARCHICAL') { // Group nodes by depth const depthGroups: Record = {}; nodes.forEach(n => { const d = n.depth; if (!depthGroups[d]) depthGroups[d] = []; depthGroups[d].push(n); }); const maxD = Math.max(...nodes.map(n => n.depth), 1); Object.entries(depthGroups).forEach(([depthStr, groupNodes]) => { const d = Number(depthStr); // Reverse layout: Depth 0 (Sink) on Right, Depth N (Origins) on Left const xPercent = 1 - (d / maxD); const x = paddingX + xPercent * (width - 2 * paddingX); groupNodes.forEach((node, idx) => { const ySpacing = (height - 2 * paddingY) / Math.max(groupNodes.length, 1); const y = paddingY + idx * ySpacing + ySpacing / 2; pos[node.id] = { x, y, depth: d }; }); }); } else { // Concentric Rings: Center is Sink (Depth 0), Outer Rings are Upstream Sources const centerX = width / 2; const centerY = height / 2; const maxRadius = Math.min(width, height) / 2 - 40; const depthGroups: Record = {}; nodes.forEach(n => { const d = n.depth; if (!depthGroups[d]) depthGroups[d] = []; depthGroups[d].push(n); }); const maxD = Math.max(...nodes.map(n => n.depth), 1); Object.entries(depthGroups).forEach(([depthStr, groupNodes]) => { const d = Number(depthStr); const radius = d === 0 ? 0 : (d / maxD) * maxRadius; groupNodes.forEach((node, idx) => { if (d === 0) { pos[node.id] = { x: centerX, y: centerY, depth: 0 }; } else { const angle = (idx / groupNodes.length) * 2 * Math.PI - Math.PI / 2; const x = centerX + Math.cos(angle) * radius; const y = centerY + Math.sin(angle) * radius; pos[node.id] = { x, y, depth: d }; } }); }); } return pos; }, [nodes, layoutMode]); const inspectedNode = nodes.find(n => n.id === inspectedNodeId) || nodes.find(n => n.id === selectedSinkId); return (
{/* Top Header & Reverse Traversal Control Bar */}

Reverse Network Topology & Upstream Traversal Engine

CONTINUOUS CHROMATIC DEPTH GRADIENT

Backpropagate and identify upstream liquidity sources, smurfing pools, and proxy relays from any target sink.

{/* Action Controls */}
{/* Traversal Summary Bar & Filters */}
{/* Metric 1: Selected Target Sink */}
Root Focus Sink
{/* Metric 2: Upstream Backpropagation Volume */}
Upstream Volume Trace
${traversalResult.totalUpstreamVolumeUsd.toLocaleString()} USD
Across {traversalResult.visitedNodeIds.size} ancestor nodes ({traversalResult.maxDepth} depth tiers)
{/* Metric 3: Critical Bottlenecks */}
Critical Cut-Set Bottlenecks
{traversalResult.bottlenecks.length} Intermediary Nodes
{traversalResult.bottlenecks.join(', ') || 'No single-point cuts'}
{/* Metric 4: Search & Depth Filter */}
Traversal Depth Limit: {maxDepthFilter} Hops
setMaxDepthFilter(Number(e.target.value))} className="w-full accent-cyan-500 cursor-pointer h-1.5 bg-slate-950 rounded" />
1 (Immediate) 6 (Deep Origin)
{/* Main Canvas / SVG Reverse Graph Area */}
{/* SVG Graph Viewport (3 Cols) */}
{/* Depth Gradient Legend Bar */}
Depth & Continuity Color Gradient:
D0 (Sink) → D1 (Mixer) → D2 (Hawala) → D3 (Smurf) → D4 (Gateway) → D5 (Origin)
{/* SVG Canvas */}
{/* Dynamic Linear Gradients for Continuous Edge Coloring */} {filteredEdges.map(edge => { const sourceDepth = nodePositions[edge.source]?.depth ?? 3; const targetDepth = nodePositions[edge.target]?.depth ?? 0; const sourceColor = getDepthColor(sourceDepth).hex; const targetColor = getDepthColor(targetDepth).hex; return ( ); })} {/* Marker Arrows */} {/* Background Grid Pattern */} {/* Draw Edges / Flows */} {filteredEdges.map(edge => { const src = nodePositions[edge.source]; const dst = nodePositions[edge.target]; if (!src || !dst) return null; const isPathActive = traversalResult.visitedEdgeIds.has(edge.id); const strokeWidth = isPathActive ? Math.min(Math.max(edge.amountUsd / 20000, 2.5), 6) : 1.5; const opacity = isPathActive ? 0.9 : 0.3; // Bezier curved connector const dx = dst.x - src.x; const dy = dst.y - src.y; const cx1 = src.x + dx * 0.5; const cy1 = src.y; const cx2 = dst.x - dx * 0.5; const cy2 = dst.y; const pathD = `M ${src.x} ${src.y} C ${cx1} ${cy1}, ${cx2} ${cy2}, ${dst.x} ${dst.y}`; return ( {/* Glowing Underlay */} {isPathActive && ( )} {/* Main Edge Path */} {/* Flow Particle Animation */} {showAnimation && isPathActive && ( )} {/* Edge Label on Midpoint */} ${(edge.amountUsd / 1000).toFixed(0)}k {edge.asset} ); })} {/* Draw Nodes */} {nodes.map(node => { const pos = nodePositions[node.id]; if (!pos) return null; const isSink = node.depth === 0 || node.id === selectedSinkId; const isInspected = node.id === inspectedNodeId; const isVisited = traversalResult.visitedNodeIds.has(node.id); const isBottleneck = node.isBottleneck; const colorDef = getDepthColor(node.depth); const nodeRadius = isSink ? 22 : isBottleneck ? 19 : 16; return ( { setInspectedNodeId(node.id); if (node.depth === 0 || !selectedSinkId) { setSelectedSinkId(node.id); } }} className="cursor-pointer transition-transform hover:scale-110 select-none" > {/* Outer Glow Halo for Active Path & Bottleneck */} {(isVisited || isInspected) && ( )} {/* Node Body Circle */} {/* Inner Badge Glyph */} {isSink ? 'SINK' : `D${node.depth}`} {/* Node Label Below */} {node.label.length > 18 ? node.label.slice(0, 16) + '...' : node.label} {/* Jurisdiction Subtitle */} {node.jurisdiction} {/* Sanction Alert Badge if Applicable */} {node.isSanctioned && ( ! )} ); })}
{/* Node Inspector Drawer & Upstream Breakdown (1 Col) */}
Entity Forensic Profile
{inspectedNode && ( DEPTH {inspectedNode.depth} )}
{inspectedNode ? (
ENTITY ALIAS / LABEL
{inspectedNode.label}
{inspectedNode.ipOrAddress}
CLASSIFICATION
{inspectedNode.entityType}
JURISDICTION
{inspectedNode.jurisdiction}
INFLOW VOLUME
${inspectedNode.totalInflowUsd.toLocaleString()}
OUTFLOW VOLUME
${inspectedNode.totalOutflowUsd.toLocaleString()}
{/* Sanctions & Risk Status */}
Threat / AML Risk Score: = 80 ? 'text-rose-400' : 'text-amber-400'}`}> {inspectedNode.riskScore}/100
= 80 ? 'bg-rose-500' : 'bg-amber-500'}`} style={{ width: `${inspectedNode.riskScore}%` }} />
{inspectedNode.isSanctioned && (
Flagged on Specially Designated Global Terrorist (SDGT) Sanctions List.
)} {inspectedNode.isBottleneck && (
Topological Cut-Set: Critical liquidity bottleneck for this operation.
)} {/* Query in SQL Database button */}
) : (
Click on any node in the canvas above to inspect details and trace ancestors.
)}
); };