#!/usr/bin/env python3
"""
📊 Simulateur de métriques temps réel pour Web Sentinel
Génère des métriques de test en temps réel pour monitoring et dashboards
"""

import sys
import json
import time
import random
import threading
from datetime import datetime, timedelta
from pathlib import Path
from typing import Dict, List, Any
from dataclasses import dataclass, asdict

# Ajouter le répertoire parent pour importer web_sentinel
sys.path.insert(0, str(Path(__file__).parent.parent))

@dataclass
class ScanMetrics:
    """Métriques d'un scan"""
    scan_id: str
    domain: str
    user_email: str
    tier: str
    start_time: datetime
    end_time: datetime = None
    duration_ms: int = 0
    findings_count: int = 0
    findings_by_severity: Dict[str, int] = None
    modules_executed: List[str] = None
    success: bool = True
    error_message: str = None

@dataclass
class SystemMetrics:
    """Métriques système"""
    timestamp: datetime
    active_scans: int
    total_scans_today: int
    avg_scan_duration_ms: float
    cpu_usage_percent: float
    memory_usage_mb: int
    active_users: int
    api_requests_per_minute: int

class MetricsSimulator:
    """Simulateur de métriques temps réel"""
    
    def __init__(self):
        self.config_dir = Path(__file__).parent / "config"
        self.metrics_dir = Path(__file__).parent / "metrics"
        self.metrics_dir.mkdir(exist_ok=True)
        
        # Charger configuration
        with open(self.config_dir / "test_config.json", 'r', encoding='utf-8') as f:
            self.config = json.load(f)
        
        with open(self.config_dir / "accounts.json", 'r', encoding='utf-8') as f:
            self.accounts = json.load(f)
        
        # État du simulateur
        self.running = False
        self.active_scans: Dict[str, ScanMetrics] = {}
        self.completed_scans: List[ScanMetrics] = []
        self.system_metrics: List[SystemMetrics] = []
        
        # Domaines de test pour simulation
        self.test_domains = [
            "example.com", "test.org", "demo.net", "sample.io",
            "staging.app", "dev.local", "api.service", "web.platform"
        ]
        
        # Modules de scan disponibles
        self.available_modules = [
            "headers", "tls", "injection", "static-analysis", "third-party"
        ]
    
    def generate_scan_id(self) -> str:
        """Générer un ID de scan unique"""
        timestamp = int(time.time() * 1000)
        random_part = random.randint(1000, 9999)
        return f"scan_{timestamp}_{random_part}"
    
    def select_random_user(self) -> Dict[str, Any]:
        """Sélectionner un utilisateur aléatoire"""
        return random.choice(self.accounts["test_accounts"])
    
    def simulate_scan_start(self) -> ScanMetrics:
        """Simuler le début d'un scan"""
        user = self.select_random_user()
        domain = random.choice(self.test_domains)
        
        scan = ScanMetrics(
            scan_id=self.generate_scan_id(),
            domain=domain,
            user_email=user["email"],
            tier=user["tier"],
            start_time=datetime.now(),
            findings_by_severity={"critical": 0, "high": 0, "medium": 0, "low": 0, "info": 0},
            modules_executed=[]
        )
        
        self.active_scans[scan.scan_id] = scan
        return scan
    
    def simulate_scan_progress(self, scan: ScanMetrics) -> None:
        """Simuler le progrès d'un scan"""
        # Durée réaliste selon le tier
        tier_durations = {
            "FREE": (2, 8),      # 2-8 secondes
            "PRO": (5, 15),      # 5-15 secondes  
            "ENTERPRISE": (10, 30),  # 10-30 secondes
            "GOVERNMENT": (15, 45),  # 15-45 secondes
            "FINANCE": (20, 60),     # 20-60 secondes
            "MEDICAL": (25, 90)      # 25-90 secondes
        }
        
        min_duration, max_duration = tier_durations.get(scan.tier, (5, 15))
        duration = random.uniform(min_duration, max_duration)
        
        # Simuler l'exécution de modules
        num_modules = random.randint(2, len(self.available_modules))
        scan.modules_executed = random.sample(self.available_modules, num_modules)
        
        # Simuler le temps d'exécution
        time.sleep(duration)
        
        # Simuler les résultats
        scan.end_time = datetime.now()
        scan.duration_ms = int((scan.end_time - scan.start_time).total_seconds() * 1000)
        
        # Générer des findings aléatoires
        total_findings = random.randint(0, 15)
        scan.findings_count = total_findings
        
        if total_findings > 0:
            remaining = total_findings
            for severity in ["critical", "high", "medium", "low", "info"]:
                if remaining > 0:
                    count = random.randint(0, min(3, remaining))
                    scan.findings_by_severity[severity] = count
                    remaining -= count
        
        # Simuler erreurs occasionnelles
        if random.random() < 0.05:  # 5% de chance d'erreur
            scan.success = False
            scan.error_message = random.choice([
                "Connection timeout", "DNS resolution failed", 
                "SSL handshake error", "Invalid response"
            ])
    
    def simulate_scan_complete(self, scan: ScanMetrics) -> None:
        """Marquer un scan comme terminé"""
        if scan.scan_id in self.active_scans:
            del self.active_scans[scan.scan_id]
        
        self.completed_scans.append(scan)
        
        # Garder seulement les 1000 derniers scans
        if len(self.completed_scans) > 1000:
            self.completed_scans = self.completed_scans[-1000:]
    
    def generate_system_metrics(self) -> SystemMetrics:
        """Générer des métriques système"""
        # Heure de pointe simulation (9h-17h augmente l'activité)
        hour = datetime.now().hour
        peak_multiplier = 2.0 if 9 <= hour <= 17 else 1.0
        
        return SystemMetrics(
            timestamp=datetime.now(),
            active_scans=len(self.active_scans),
            total_scans_today=len([s for s in self.completed_scans 
                                 if s.start_time.date() == datetime.now().date()]),
            avg_scan_duration_ms=self.calculate_avg_duration(),
            cpu_usage_percent=random.uniform(10, 80) * peak_multiplier,
            memory_usage_mb=random.randint(500, 2048),
            active_users=random.randint(1, 20) * int(peak_multiplier),
            api_requests_per_minute=random.randint(50, 500) * int(peak_multiplier)
        )
    
    def calculate_avg_duration(self) -> float:
        """Calculer la durée moyenne des scans"""
        if not self.completed_scans:
            return 0.0
        
        recent_scans = [s for s in self.completed_scans 
                       if s.end_time and (datetime.now() - s.end_time).total_seconds() < 3600]
        
        if not recent_scans:
            return 0.0
        
        return sum(s.duration_ms for s in recent_scans) / len(recent_scans)
    
    def save_metrics_snapshot(self) -> None:
        """Sauvegarder un snapshot des métriques"""
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        snapshot_file = self.metrics_dir / f"metrics_snapshot_{timestamp}.json"
        
        snapshot = {
            "timestamp": datetime.now().isoformat(),
            "active_scans": [asdict(scan) for scan in self.active_scans.values()],
            "recent_completed": [asdict(scan) for scan in self.completed_scans[-100:]],
            "system_metrics": asdict(self.generate_system_metrics())
        }
        
        # Convertir les datetime en strings pour JSON
        def convert_datetime(obj):
            if isinstance(obj, datetime):
                return obj.isoformat()
            return obj
        
        with open(snapshot_file, 'w', encoding='utf-8') as f:
            json.dump(snapshot, f, indent=2, default=convert_datetime, ensure_ascii=False)
    
    def run_simulation_worker(self) -> None:
        """Worker thread pour simuler des scans"""
        while self.running:
            # Probabilité de démarrer un nouveau scan
            if random.random() < 0.3 and len(self.active_scans) < 10:
                scan = self.simulate_scan_start()
                print(f"🚀 Scan démarré: {scan.scan_id} - {scan.domain} ({scan.tier})")
                
                # Exécuter le scan dans un thread séparé
                def run_scan(s):
                    self.simulate_scan_progress(s)
                    self.simulate_scan_complete(s)
                    print(f"✅ Scan terminé: {s.scan_id} - {s.findings_count} findings ({s.duration_ms}ms)")
                
                threading.Thread(target=run_scan, args=(scan,), daemon=True).start()
            
            time.sleep(random.uniform(1, 3))  # Attendre 1-3 secondes
    
    def run_metrics_collector(self) -> None:
        """Collecter les métriques système périodiquement"""
        while self.running:
            metrics = self.generate_system_metrics()
            self.system_metrics.append(metrics)
            
            # Garder seulement les 24h de métriques (1 point par minute)
            if len(self.system_metrics) > 1440:
                self.system_metrics = self.system_metrics[-1440:]
            
            # Sauvegarder snapshot toutes les 10 minutes
            if len(self.system_metrics) % 10 == 0:
                self.save_metrics_snapshot()
                print(f"📊 Snapshot sauvegardé - Scans actifs: {metrics.active_scans}")
            
            time.sleep(60)  # Collecter toutes les minutes
    
    def start_simulation(self) -> None:
        """Démarrer la simulation"""
        print("📊 SIMULATEUR MÉTRIQUES WEB SENTINEL")
        print("=" * 50)
        print("🚀 Démarrage simulation...")
        
        self.running = True
        
        # Démarrer les threads workers
        simulation_thread = threading.Thread(target=self.run_simulation_worker, daemon=True)
        metrics_thread = threading.Thread(target=self.run_metrics_collector, daemon=True)
        
        simulation_thread.start()
        metrics_thread.start()
        
        print("✅ Simulation active!")
        print("   - Scans automatiques toutes les 1-3 secondes")
        print("   - Métriques collectées toutes les minutes")
        print("   - Snapshots sauvegardés toutes les 10 minutes")
        print("   - Appuyez sur Ctrl+C pour arrêter")
        
        try:
            while True:
                time.sleep(5)
                # Afficher statut périodiquement
                print(f"📈 Scans actifs: {len(self.active_scans)}, "
                      f"Complétés: {len(self.completed_scans)}, "
                      f"Métriques: {len(self.system_metrics)}")
        
        except KeyboardInterrupt:
            print("\n🛑 Arrêt simulation...")
            self.running = False
            time.sleep(2)
            print("✅ Simulation arrêtée")
    
    def get_dashboard_data(self) -> Dict[str, Any]:
        """Obtenir les données pour un dashboard"""
        current_metrics = self.generate_system_metrics()
        
        # Statistiques des scans récents (dernière heure)
        recent_scans = [s for s in self.completed_scans 
                       if s.end_time and (datetime.now() - s.end_time).total_seconds() < 3600]
        
        success_rate = 0
        if recent_scans:
            success_rate = sum(1 for s in recent_scans if s.success) / len(recent_scans) * 100
        
        # Distribution par tier
        tier_distribution = {}
        for scan in recent_scans:
            tier_distribution[scan.tier] = tier_distribution.get(scan.tier, 0) + 1
        
        return {
            "timestamp": datetime.now().isoformat(),
            "current_metrics": asdict(current_metrics),
            "active_scans_detail": [asdict(scan) for scan in self.active_scans.values()],
            "recent_stats": {
                "total_scans": len(recent_scans),
                "success_rate": round(success_rate, 2),
                "avg_duration_ms": self.calculate_avg_duration(),
                "tier_distribution": tier_distribution
            },
            "findings_summary": self.get_findings_summary()
        }
    
    def get_findings_summary(self) -> Dict[str, int]:
        """Résumé des findings des scans récents"""
        recent_scans = [s for s in self.completed_scans 
                       if s.end_time and (datetime.now() - s.end_time).total_seconds() < 3600]
        
        summary = {"critical": 0, "high": 0, "medium": 0, "low": 0, "info": 0}
        
        for scan in recent_scans:
            if scan.findings_by_severity:
                for severity, count in scan.findings_by_severity.items():
                    summary[severity] += count
        
        return summary


def main():
    import argparse
    
    parser = argparse.ArgumentParser(description="📊 Simulateur métriques Web Sentinel")
    parser.add_argument("--start", action="store_true", help="Démarrer simulation temps réel")
    parser.add_argument("--dashboard", action="store_true", help="Afficher données dashboard")
    parser.add_argument("--snapshot", action="store_true", help="Créer snapshot actuel")
    parser.add_argument("--duration", type=int, default=0, help="Durée simulation (minutes)")
    
    args = parser.parse_args()
    
    simulator = MetricsSimulator()
    
    if args.start:
        if args.duration > 0:
            print(f"⏱️ Simulation limitée à {args.duration} minutes")
            threading.Timer(args.duration * 60, lambda: setattr(simulator, 'running', False)).start()
        
        simulator.start_simulation()
    
    elif args.dashboard:
        dashboard = simulator.get_dashboard_data()
        print("📊 DONNÉES DASHBOARD")
        print("=" * 50)
        print(json.dumps(dashboard, indent=2, default=str, ensure_ascii=False))
    
    elif args.snapshot:
        simulator.save_metrics_snapshot()
        print("✅ Snapshot créé")
    
    else:
        print("📊 AIDE RAPIDE:")
        print("  --start                    # Démarrer simulation temps réel")
        print("  --start --duration 30      # Simulation 30 minutes")
        print("  --dashboard               # Afficher données dashboard")
        print("  --snapshot                # Créer snapshot")


if __name__ == "__main__":
    main()