import { useState, useEffect, useRef } from 'react'; import { motion } from 'framer-motion'; import { useCreateSimulation, useBenchmarkDatasetsStatus, useDownloadBenchmarkDataset, useBenchmarkDownloadStatus, } from '../../api/queries'; import { DEFAULT_SIMULATION_CONFIG } from '../../utils/constants'; import type { SimulationConfig } from '../../api/types'; interface SimulationControlsProps { onSimulationCreated: (id: string) => void; } export default function SimulationControls({ onSimulationCreated }: SimulationControlsProps) { const [config, setConfig] = useState>(DEFAULT_SIMULATION_CONFIG); const [isLargeMonitor, setIsLargeMonitor] = useState(() => { return typeof window !== 'undefined' && window.innerWidth >= 1600 && window.innerHeight >= 900; }); const [isExpanded, setIsExpanded] = useState(false); const createMutation = useCreateSimulation(); const { data: benchmarkStatus } = useBenchmarkDatasetsStatus(); const isSubmittingRef = useRef(false); const selectedDataset = config.dataset; const isBenchmark = selectedDataset && selectedDataset !== 'synthetic'; const hasRealFiles = isBenchmark ? Boolean(benchmarkStatus?.[selectedDataset]?.has_real_files) : false; const effectiveMode = config.dataset_mode ?? (hasRealFiles ? 'real' : 'synthetic'); const serverHasKaggleCreds = Boolean( benchmarkStatus?._meta?.kaggle_configured ?? (selectedDataset ? benchmarkStatus?.[selectedDataset]?.kaggle_configured : false) ); // Downloader state for Hugging Face Spaces & container environments const [showDownloadPanel, setShowDownloadPanel] = useState(false); const [downloadSource, setDownloadSource] = useState<'auto' | 'mirror' | 'kaggle'>('auto'); const [kaggleUsername, setKaggleUsername] = useState(() => { return typeof localStorage !== 'undefined' ? localStorage.getItem('cfi_kaggle_username') || '' : ''; }); const [kaggleKey, setKaggleKey] = useState(() => { return typeof localStorage !== 'undefined' ? localStorage.getItem('cfi_kaggle_key') || '' : ''; }); const downloadMutation = useDownloadBenchmarkDataset(); const { data: downloadStatus } = useBenchmarkDownloadStatus( isBenchmark ? selectedDataset : undefined, Boolean(isBenchmark && !hasRealFiles) ); const isDownloading = downloadStatus?.status === 'in_progress' || downloadMutation.isPending; useEffect(() => { if (downloadStatus?.status === 'completed' && isBenchmark) { setConfig((prev) => ({ ...prev, dataset_mode: 'real' })); setShowDownloadPanel(false); } }, [downloadStatus?.status, isBenchmark]); const handleStartDownload = () => { if (!selectedDataset || selectedDataset === 'synthetic') return; if (typeof localStorage !== 'undefined') { if (kaggleUsername) localStorage.setItem('cfi_kaggle_username', kaggleUsername); if (kaggleKey) localStorage.setItem('cfi_kaggle_key', kaggleKey); } downloadMutation.mutate({ dataset: selectedDataset, kaggle_username: kaggleUsername || undefined, kaggle_key: kaggleKey || undefined, source: downloadSource, }); }; useEffect(() => { if (typeof window === 'undefined') return; const handleResize = () => { setIsLargeMonitor(window.innerWidth >= 1600 && window.innerHeight >= 900); }; window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); const handleStart = () => { if (createMutation.isPending || isSubmittingRef.current) return; isSubmittingRef.current = true; const clientOpId = 'sim_op_' + (typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).substring(2)); createMutation.mutate({ ...config, ...(isBenchmark ? { dataset_mode: effectiveMode } : {}), clientOperationId: clientOpId, idempotencyKey: clientOpId, }, { onSuccess: (data) => { onSimulationCreated(data.id); }, onSettled: () => { isSubmittingRef.current = false; }, }); }; const updateConfig = (key: K, value: SimulationConfig[K]) => { setConfig((prev) => ({ ...prev, [key]: value })); }; return (

Simulation Configuration

{!isLargeMonitor && ( )}
{/* Scrollable Settings Form */}
{/* Core Settings - always visible */}
updateConfig('num_rounds', parseInt(e.target.value) || 10)} min={1} max={100} className="w-full bg-[var(--color-bg-elevated)] border border-[var(--color-border)] rounded-md px-3 py-2 text-sm text-[var(--color-text-primary)] font-mono focus:outline-none focus:border-[var(--color-accent-indigo)] transition-colors" />
updateConfig('local_epochs', parseInt(e.target.value) || 3)} min={1} max={20} className="w-full bg-[var(--color-bg-elevated)] border border-[var(--color-border)] rounded-md px-3 py-2 text-sm text-[var(--color-text-primary)] font-mono focus:outline-none focus:border-[var(--color-accent-indigo)] transition-colors" />
updateConfig('learning_rate', parseFloat(e.target.value) || 0.001)} step={0.0001} min={0.0001} max={1} className="w-full bg-[var(--color-bg-elevated)] border border-[var(--color-border)] rounded-md px-3 py-2 text-sm text-[var(--color-text-primary)] font-mono focus:outline-none focus:border-[var(--color-accent-indigo)] transition-colors" />
{/* Advanced Settings */} {(isExpanded || isLargeMonitor) && ( {/* Benchmark Dataset Selection */}

Benchmark Dataset {isBenchmark ? effectiveMode === 'real' ? '✓ Real Kaggle Dataset' : '⚡ Synthetic Fixture' : 'Synthetic Generator'}

{isBenchmark && (
Dataset Source Mode:

{effectiveMode === 'real' ? hasRealFiles ? '✓ Real Kaggle benchmark dataset files verified on disk. Partitioned Non-IID across banks with dynamic PyTorch model sizing.' : '⚠️ Real files not detected in storage. Training in real mode will fail closed unless physical CSV/Parquet files are mounted or downloaded.' : '⚡ High-fidelity synthetic benchmark fixture preserving exact dataset schema and feature columns for fast offline testing.'}

{/* Benchmark Downloader Card for Hugging Face Spaces & Cloud/Container environments */} {selectedDataset && !hasRealFiles && (
⚠️ Real files missing on server
{!isDownloading && ( )}
{/* Live Progress Bar when downloading */} {isDownloading && (
{downloadStatus?.message || 'Downloading dataset files...'} {downloadStatus?.percent ?? 0}%

Files are saved directly to server storage (/app/storage/datasets). Once complete, real federated training is unlocked permanently.

)} {/* Failure Alert */} {downloadStatus?.status === 'failed' && (

Download Error:

{downloadStatus.error || downloadStatus.message}

)} {/* Config Panel for Download */} {showDownloadPanel && !isDownloading && (
Source strategy for {selectedDataset.toUpperCase()}:
{(selectedDataset === 'creditcard' || selectedDataset === 'elliptic') && ( )}
{serverHasKaggleCreds ? (
🔒 Hugging Face Kaggle Secrets detected. Server ready for 1-click download!
) : ( (downloadSource === 'kaggle' || selectedDataset === 'paysim' || selectedDataset === 'ieee_cis') && (
Optional if set in HF Secrets (KAGGLE_USERNAME / KAGGLE_KEY) Get Token ↗
setKaggleUsername(e.target.value)} className="px-2 py-1 text-xs bg-[var(--color-bg-base)] border border-[var(--color-border)] rounded text-[var(--color-text-primary)]" /> setKaggleKey(e.target.value)} className="px-2 py-1 text-xs bg-[var(--color-bg-base)] border border-[var(--color-border)] rounded text-[var(--color-text-primary)]" />
) )}
)}
)}
)}
{/* FL Engine Selection */}

FL Engine

{config.fl_engine_type === 'flower' && (

⚡ Flower mode uses FedAvg only. Dropout, latency, poisoning, and Byzantine-robust aggregation are disabled.

)}
{/* Failure Simulation */}

Failure Simulation {config.fl_engine_type === 'flower' && (Flower N/A)}

{config.enable_dropout_simulation && (
updateConfig('dropout_probability', parseInt(e.target.value) / 100)} className="w-full accent-[var(--color-accent-indigo)]" />
)}
{/* Privacy */}

Privacy Mechanism

{(config.privacy_mechanism === 'differential_privacy' || config.privacy_mechanism === 'both') && (
updateConfig('dp_epsilon', parseFloat(e.target.value))} className="w-full accent-[var(--color-accent-indigo)]" />

Lower ε = stronger privacy, more noise, lower utility

)} {(config.privacy_mechanism === 'differential_privacy' || config.privacy_mechanism === 'both') && (

Opacus uses Meta AI's library for industry-standard per-sample gradient clipping

)}
{/* Aggregation Strategy */}

Aggregation Strategy {config.fl_engine_type === 'flower' && (FedAvg only)}

{config.fl_engine_type === 'flower' ? 'Flower uses its built-in FedAvg implementation' : 'FedProx, FedYogi & SCAFFOLD control client drift and adaptive convergence'}

{config.aggregation_method === 'fed_prox' && (
updateConfig('fedprox_mu', parseFloat(e.target.value))} className="w-full accent-[var(--color-accent-indigo)]" />

Adds ½μ||w - w_t||² proximal penalty to local loss, curbing client drift across Non-IID bank data.

)}
{/* Adversarial Simulation */}

Adversarial Simulation {config.fl_engine_type === 'flower' && (Flower N/A)}

{config.enable_poisoning_simulation && (
updateConfig('poisoning_scale', parseFloat(e.target.value))} className="w-full accent-[var(--color-status-error)]" />

Higher scale = more aggressive attack noise injected into model weights

)}
{/* Active Defense & Adversarial ML Training */}

Active Defense & Adversarial Training

{config.enable_adversarial_training && (
updateConfig('adversarial_epsilon', parseFloat(e.target.value))} className="w-full accent-cyan-500" />
)}
{/* Regulatory Fairness & Bias Mitigation */}

Regulatory AI Compliance & Fairness

{config.enable_bias_mitigation && (
updateConfig('fairness_lambda', parseFloat(e.target.value))} className="w-full accent-[var(--color-accent-indigo)]" />

Higher weight pushes model parameters to have zero covariance with sensitive attributes (nationality/region).

)}
{/* Hardware & Cryptographic Isolation */}

Hardware & Cryptographic Isolation

TEE runs secure summation inside isolated enclaves; FHE uses encrypted parameter addition.

{/* Real-Time Streaming GNN Settings */}

Graph Neural Network Dynamics

Enables online self-supervised training on transaction graph updates as payments stream in.

{/* Web3 & CBDC Smart Contract Incentive Settlement */}

Web3 & CBDC Smart Contract Settlement

{config.enable_web3_settlement && (

Disburses token payouts automatically to consortium bank wallets upon simulation completion based on LOO Shapley scores.

)}
{/* Data Volume */}

Data Volume

{(['bank_a_transactions', 'bank_b_transactions', 'bank_c_transactions'] as const).map((key, i) => (
updateConfig(key, parseInt(e.target.value) || 10000)} min={1000} max={200000} step={1000} className="w-full bg-[var(--color-bg-elevated)] border border-[var(--color-border)] rounded-md px-3 py-2 text-sm text-[var(--color-text-primary)] font-mono focus:outline-none focus:border-[var(--color-accent-indigo)] transition-colors" />
))}
)}
{/* Start Button */} {createMutation.isError && (

Failed to start simulation. Is the backend running?

)}
); }