#!/usr/bin/env python3
"""
🎯 Orchestrateur de tests complet pour Web Sentinel
Lance tous les types de tests: unité, intégration, licence, performance
"""

import sys
import os
import json
import time
import subprocess
import threading
from pathlib import Path
from datetime import datetime, timedelta
from typing import Dict, List, Any, Optional
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 TestResult:
    """Résultat d'un test"""
    test_name: str
    test_type: str
    start_time: datetime
    end_time: datetime
    duration_ms: int
    success: bool
    error_message: Optional[str] = None
    details: Optional[Dict[str, Any]] = None

@dataclass
class TestSuite:
    """Suite de tests"""
    name: str
    description: str
    tests: List[str]
    dependencies: List[str] = None
    timeout_minutes: int = 10

class TestOrchestrator:
    """Orchestrateur de tests complets"""
    
    def __init__(self):
        self.base_dir = Path(__file__).parent
        self.project_dir = self.base_dir.parent
        self.logs_dir = self.base_dir / "logs"
        self.reports_dir = self.base_dir / "reports"
        
        # Créer répertoires
        self.logs_dir.mkdir(exist_ok=True)
        self.reports_dir.mkdir(exist_ok=True)
        
        # État des tests
        self.test_results: List[TestResult] = []
        self.current_suite: Optional[str] = None
        
        # Configuration
        with open(self.base_dir / "config" / "test_config.json", 'r', encoding='utf-8') as f:
            self.config = json.load(f)
    
    def get_test_suites(self) -> Dict[str, TestSuite]:
        """Définir toutes les suites de tests"""
        return {
            "unit": TestSuite(
                name="Tests unitaires",
                description="Tests des composants individuels",
                tests=[
                    "test_license_validation",
                    "test_crypto_manager", 
                    "test_account_manager",
                    "test_scan_modules",
                    "test_reporting"
                ],
                timeout_minutes=5
            ),
            "integration": TestSuite(
                name="Tests d'intégration",
                description="Tests d'intégration des composants",
                tests=[
                    "test_license_integration",
                    "test_database_integration",
                    "test_api_integration",
                    "test_scanner_pipeline"
                ],
                dependencies=["unit"],
                timeout_minutes=10
            ),
            "license": TestSuite(
                name="Tests de licence",
                description="Tests complets du système de licence",
                tests=[
                    "test_all_tiers",
                    "test_expiry_scenarios",
                    "test_upgrade_paths",
                    "test_security_validation"
                ],
                dependencies=["unit", "integration"],
                timeout_minutes=15
            ),
            "performance": TestSuite(
                name="Tests de performance",
                description="Tests de charge et performance",
                tests=[
                    "test_scan_performance",
                    "test_concurrent_scans",
                    "test_memory_usage",
                    "test_database_performance"
                ],
                dependencies=["integration"],
                timeout_minutes=20
            ),
            "security": TestSuite(
                name="Tests de sécurité",
                description="Tests de sécurité et pénétration",
                tests=[
                    "test_injection_resistance",
                    "test_auth_bypass",
                    "test_data_exposure",
                    "test_license_tampering"
                ],
                dependencies=["integration"],
                timeout_minutes=30
            ),
            "e2e": TestSuite(
                name="Tests end-to-end",
                description="Tests bout en bout complets",
                tests=[
                    "test_user_journey_free",
                    "test_user_journey_pro",
                    "test_user_journey_enterprise",
                    "test_admin_workflows"
                ],
                dependencies=["unit", "integration", "license"],
                timeout_minutes=25
            )
        }
    
    def run_unit_tests(self) -> List[TestResult]:
        """Exécuter les tests unitaires"""
        results = []
        
        unit_tests = [
            ("test_license_validation", self.test_license_validation),
            ("test_crypto_manager", self.test_crypto_manager),
            ("test_account_manager", self.test_account_manager),
            ("test_scan_modules", self.test_scan_modules),
            ("test_reporting", self.test_reporting)
        ]
        
        for test_name, test_func in unit_tests:
            result = self.execute_test(test_name, "unit", test_func)
            results.append(result)
        
        return results
    
    def run_integration_tests(self) -> List[TestResult]:
        """Exécuter les tests d'intégration"""
        results = []
        
        integration_tests = [
            ("test_license_integration", self.test_license_integration),
            ("test_database_integration", self.test_database_integration),
            ("test_api_integration", self.test_api_integration),
            ("test_scanner_pipeline", self.test_scanner_pipeline)
        ]
        
        for test_name, test_func in integration_tests:
            result = self.execute_test(test_name, "integration", test_func)
            results.append(result)
        
        return results
    
    def run_license_tests(self) -> List[TestResult]:
        """Exécuter les tests de licence complets"""
        results = []
        
        # Utiliser les outils existants
        license_tests = [
            ("test_all_tiers", self.test_all_tiers),
            ("test_expiry_scenarios", self.test_expiry_scenarios),
            ("test_upgrade_paths", self.test_upgrade_paths),
            ("test_security_validation", self.test_security_validation)
        ]
        
        for test_name, test_func in license_tests:
            result = self.execute_test(test_name, "license", test_func)
            results.append(result)
        
        return results
    
    def execute_test(self, test_name: str, test_type: str, test_func) -> TestResult:
        """Exécuter un test individuel"""
        start_time = datetime.now()
        print(f"🧪 Exécution: {test_name}")
        
        try:
            details = test_func()
            end_time = datetime.now()
            duration_ms = int((end_time - start_time).total_seconds() * 1000)
            
            result = TestResult(
                test_name=test_name,
                test_type=test_type,
                start_time=start_time,
                end_time=end_time,
                duration_ms=duration_ms,
                success=True,
                details=details
            )
            
            print(f"✅ {test_name} - {duration_ms}ms")
            
        except Exception as e:
            end_time = datetime.now()
            duration_ms = int((end_time - start_time).total_seconds() * 1000)
            
            result = TestResult(
                test_name=test_name,
                test_type=test_type,
                start_time=start_time,
                end_time=end_time,
                duration_ms=duration_ms,
                success=False,
                error_message=str(e)
            )
            
            print(f"❌ {test_name} - {str(e)}")
        
        self.test_results.append(result)
        return result
    
    # Tests unitaires
    def test_license_validation(self) -> Dict[str, Any]:
        """Test validation de licence"""
        from test_accounts import TestAccountManager
        
        account_manager = TestAccountManager()
        
        # Test compte bruno
        account = account_manager.get_account("bruno@taaazzz-prog.fr")
        assert account is not None, "Compte bruno introuvable"
        assert account["tier"] == "PRO", f"Tier incorrect: {account['tier']}"
        
        return {"account_validated": True, "tier": account["tier"]}
    
    def test_crypto_manager(self) -> Dict[str, Any]:
        """Test gestionnaire cryptographique"""
        # Import du système crypto
        sys.path.insert(0, str(self.project_dir))
        from web_sentinel.license.crypto_manager import CryptoManager
        
        crypto = CryptoManager()
        
        # Test chiffrement/déchiffrement
        test_data = "test_data_12345"
        encrypted = crypto.encrypt_data(test_data)
        decrypted = crypto.decrypt_data(encrypted)
        
        assert decrypted == test_data, "Échec chiffrement/déchiffrement"
        
        return {"encryption_test": True, "data_length": len(encrypted)}
    
    def test_account_manager(self) -> Dict[str, Any]:
        """Test gestionnaire de comptes"""
        from test_accounts import TestAccountManager
        
        manager = TestAccountManager()
        
        # Test création compte temporaire
        test_email = f"test_{int(time.time())}@example.com"
        manager.create_temporary_account(test_email, "FREE")
        
        account = manager.get_account(test_email)
        assert account is not None, "Compte temporaire non créé"
        
        # Nettoyage
        manager.delete_account(test_email)
        
        return {"temp_account_test": True, "email": test_email}
    
    def test_scan_modules(self) -> Dict[str, Any]:
        """Test modules de scan"""
        # Test avec un domaine de test
        test_domain = "httpbin.org"
        modules_tested = []
        
        try:
            # Simuler test de module headers
            import requests
            response = requests.get(f"https://{test_domain}", timeout=10)
            if response.status_code == 200:
                modules_tested.append("connection_test")
            
            # Simuler analyse headers
            headers = response.headers
            security_headers = ["X-Frame-Options", "X-Content-Type-Options"]
            found_headers = [h for h in security_headers if h in headers]
            modules_tested.append("headers_analysis")
            
        except Exception as e:
            raise Exception(f"Échec test modules: {e}")
        
        return {"modules_tested": modules_tested, "domain": test_domain}
    
    def test_reporting(self) -> Dict[str, Any]:
        """Test système de rapport"""
        from generate_test_license import TestLicenseGenerator
        
        generator = TestLicenseGenerator()
        
        # Créer un rapport de test
        report_data = {
            "test_type": "unit_test",
            "timestamp": datetime.now().isoformat(),
            "results": ["test1", "test2"]
        }
        
        report_file = self.reports_dir / "unit_test_report.json"
        with open(report_file, 'w', encoding='utf-8') as f:
            json.dump(report_data, f, indent=2)
        
        assert report_file.exists(), "Rapport non créé"
        
        return {"report_created": True, "file": str(report_file)}
    
    # Tests d'intégration
    def test_license_integration(self) -> Dict[str, Any]:
        """Test intégration complète licence"""
        from generate_test_license import TestLicenseGenerator
        from test_accounts import TestAccountManager
        
        generator = TestLicenseGenerator()
        manager = TestAccountManager()
        
        # Test génération et validation
        license_data = generator.generate_test_license("bruno@taaazzz-prog.fr", "PRO")
        assert license_data["success"], "Échec génération licence"
        
        # Test validation
        validation = generator.validate_license(license_data["license_file"])
        assert validation["valid"], "Licence générée invalide"
        
        return {"license_generated": True, "validation_passed": True}
    
    def test_database_integration(self) -> Dict[str, Any]:
        """Test intégration base de données"""
        from database_setup import TestDatabaseSetup
        
        db_setup = TestDatabaseSetup()
        
        # Test connexion
        try:
            conn = db_setup.create_connection(include_db=False)
            conn.close()
            connection_ok = True
        except Exception:
            connection_ok = False
        
        # Test stats utilisateur
        stats = db_setup.get_user_stats("bruno@taaazzz-prog.fr")
        stats_ok = len(stats) > 0
        
        return {"connection": connection_ok, "stats_retrieval": stats_ok}
    
    def test_api_integration(self) -> Dict[str, Any]:
        """Test intégration API"""
        # Test simulation d'API
        api_endpoints = [
            "/api/scan",
            "/api/license/validate", 
            "/api/account/info",
            "/api/reports"
        ]
        
        tested_endpoints = []
        for endpoint in api_endpoints:
            # Simuler test endpoint
            tested_endpoints.append(endpoint)
        
        return {"endpoints_tested": tested_endpoints, "total": len(tested_endpoints)}
    
    def test_scanner_pipeline(self) -> Dict[str, Any]:
        """Test pipeline complet de scan"""
        from test_scenarios import TestScenarios
        
        scenarios = TestScenarios()
        
        # Exécuter un scenario de test
        result = scenarios.run_license_expiry_test()
        assert result["success"], "Échec scenario licence"
        
        return {"pipeline_test": True, "scenario": "license_expiry"}
    
    # Tests de licence spécialisés
    def test_all_tiers(self) -> Dict[str, Any]:
        """Test tous les tiers de licence"""
        from test_scenarios import TestScenarios
        
        scenarios = TestScenarios()
        tiers_tested = []
        
        for tier in ["FREE", "PRO", "ENTERPRISE", "GOVERNMENT", "FINANCE", "MEDICAL"]:
            try:
                result = scenarios.test_tier_features(tier)
                if result.get("success", False):
                    tiers_tested.append(tier)
            except Exception:
                pass  # Continuer avec les autres tiers
        
        return {"tiers_tested": tiers_tested, "total_tiers": 6}
    
    def test_expiry_scenarios(self) -> Dict[str, Any]:
        """Test scénarios d'expiration"""
        from test_scenarios import TestScenarios
        
        scenarios = TestScenarios()
        
        # Test expiration
        result = scenarios.run_license_expiry_test()
        
        return {"expiry_test": result.get("success", False)}
    
    def test_upgrade_paths(self) -> Dict[str, Any]:
        """Test chemins de mise à niveau"""
        from test_scenarios import TestScenarios
        
        scenarios = TestScenarios()
        
        # Test upgrade PRO -> ENTERPRISE
        result = scenarios.run_premium_upgrade_test()
        
        return {"upgrade_test": result.get("success", False)}
    
    def test_security_validation(self) -> Dict[str, Any]:
        """Test validation sécurité licence"""
        from generate_test_license import TestLicenseGenerator
        
        generator = TestLicenseGenerator()
        
        # Test licence falsifiée
        fake_license = "fake_license_content"
        validation = generator.validate_license(fake_license)
        
        # Doit échouer pour fausse licence
        security_ok = not validation["valid"]
        
        return {"fake_license_rejected": security_ok}
    
    def run_test_suite(self, suite_name: str) -> Dict[str, Any]:
        """Exécuter une suite de tests complète"""
        suites = self.get_test_suites()
        
        if suite_name not in suites:
            raise ValueError(f"Suite inconnue: {suite_name}")
        
        suite = suites[suite_name]
        self.current_suite = suite_name
        
        print(f"🧪 SUITE: {suite.name}")
        print(f"📝 {suite.description}")
        print("=" * 60)
        
        start_time = datetime.now()
        
        # Vérifier dépendances
        if suite.dependencies:
            print(f"📋 Dépendances: {', '.join(suite.dependencies)}")
        
        # Exécuter tests selon la suite
        if suite_name == "unit":
            results = self.run_unit_tests()
        elif suite_name == "integration":
            results = self.run_integration_tests()
        elif suite_name == "license":
            results = self.run_license_tests()
        else:
            # Suites non encore implémentées
            results = []
            print(f"⚠️ Suite {suite_name} non encore implémentée")
        
        end_time = datetime.now()
        duration = end_time - start_time
        
        # Statistiques
        success_count = sum(1 for r in results if r.success)
        total_count = len(results)
        success_rate = (success_count / total_count * 100) if total_count > 0 else 0
        
        suite_result = {
            "suite_name": suite_name,
            "start_time": start_time.isoformat(),
            "end_time": end_time.isoformat(),
            "duration_seconds": duration.total_seconds(),
            "total_tests": total_count,
            "successful_tests": success_count,
            "failed_tests": total_count - success_count,
            "success_rate": round(success_rate, 2),
            "test_results": [asdict(r) for r in results]
        }
        
        print("=" * 60)
        print(f"✅ SUITE TERMINÉE: {suite.name}")
        print(f"📊 Résultats: {success_count}/{total_count} tests réussis ({success_rate:.1f}%)")
        print(f"⏱️ Durée: {duration.total_seconds():.1f} secondes")
        
        return suite_result
    
    def run_all_tests(self) -> Dict[str, Any]:
        """Exécuter toutes les suites de tests"""
        print("🎯 ORCHESTRATEUR TESTS COMPLET")
        print("=" * 80)
        
        start_time = datetime.now()
        all_results = {}
        
        # Ordre d'exécution des suites
        execution_order = ["unit", "integration", "license"]
        
        for suite_name in execution_order:
            try:
                suite_result = self.run_test_suite(suite_name)
                all_results[suite_name] = suite_result
                
                # Arrêter si une suite critique échoue
                if suite_result["success_rate"] < 50 and suite_name in ["unit", "integration"]:
                    print(f"⚠️ Suite critique {suite_name} échouée - arrêt des tests")
                    break
                    
            except Exception as e:
                print(f"❌ Erreur suite {suite_name}: {e}")
                all_results[suite_name] = {"error": str(e)}
        
        end_time = datetime.now()
        total_duration = end_time - start_time
        
        # Rapport global
        total_tests = sum(r.get("total_tests", 0) for r in all_results.values() if isinstance(r, dict))
        total_successful = sum(r.get("successful_tests", 0) for r in all_results.values() if isinstance(r, dict))
        global_success_rate = (total_successful / total_tests * 100) if total_tests > 0 else 0
        
        global_report = {
            "execution_timestamp": start_time.isoformat(),
            "total_duration_minutes": round(total_duration.total_seconds() / 60, 2),
            "suites_executed": len(all_results),
            "total_tests": total_tests,
            "total_successful": total_successful,
            "global_success_rate": round(global_success_rate, 2),
            "suite_results": all_results
        }
        
        # Sauvegarder rapport
        report_file = self.reports_dir / f"full_test_report_{start_time.strftime('%Y%m%d_%H%M%S')}.json"
        with open(report_file, 'w', encoding='utf-8') as f:
            json.dump(global_report, f, indent=2, ensure_ascii=False)
        
        print("=" * 80)
        print("🏁 TESTS COMPLETS TERMINÉS")
        print(f"📊 Résultats globaux: {total_successful}/{total_tests} tests réussis ({global_success_rate:.1f}%)")
        print(f"⏱️ Durée totale: {total_duration.total_seconds()/60:.1f} minutes")
        print(f"📄 Rapport: {report_file}")
        
        return global_report


def main():
    import argparse
    
    parser = argparse.ArgumentParser(description="🎯 Orchestrateur tests Web Sentinel")
    parser.add_argument("--all", action="store_true", help="Exécuter tous les tests")
    parser.add_argument("--suite", choices=["unit", "integration", "license", "performance", "security", "e2e"], 
                       help="Exécuter une suite spécifique")
    parser.add_argument("--list", action="store_true", help="Lister les suites disponibles")
    
    args = parser.parse_args()
    
    orchestrator = TestOrchestrator()
    
    if args.list:
        suites = orchestrator.get_test_suites()
        print("🧪 SUITES DE TESTS DISPONIBLES")
        print("=" * 50)
        for name, suite in suites.items():
            deps = f" (dépend de: {', '.join(suite.dependencies)})" if suite.dependencies else ""
            print(f"   {name}: {suite.description}{deps}")
    
    elif args.all:
        orchestrator.run_all_tests()
    
    elif args.suite:
        orchestrator.run_test_suite(args.suite)
    
    else:
        print("🎯 AIDE RAPIDE:")
        print("  --all                    # Exécuter tous les tests")
        print("  --suite unit            # Exécuter tests unitaires")
        print("  --suite integration     # Exécuter tests d'intégration")
        print("  --suite license         # Exécuter tests de licence")
        print("  --list                  # Lister suites disponibles")


if __name__ == "__main__":
    main()