#!/usr/bin/env python3
# Copyright (c) Subzero Labs, Inc.
# SPDX-License-Identifier: Apache-2.0

"""
Comprehensive test runner for the Rialo Python CDK.

This script runs all tests, benchmarks, and generates reports for Phase 4 compliance.
"""

import argparse
import json
import os
import sys
import time
import subprocess
import tempfile
from pathlib import Path
from typing import Dict, List, Optional, Any

try:
    import pytest
    import psutil
except ImportError:
    print("Required packages not installed. Run: pip install pytest psutil")
    sys.exit(1)


class TestRunner:
    """Comprehensive test runner for Rialo Python CDK."""

    def __init__(self, project_root: Path):
        self.project_root = project_root
        self.tests_dir = project_root / "tests"
        self.results = {
            "timestamp": time.time(),
            "system_info": self.get_system_info(),
            "test_results": {},
            "performance_results": {},
            "memory_results": {},
            "coverage_results": {},
        }

    def get_system_info(self) -> Dict[str, Any]:
        """Get system information for test context."""
        return {
            "python_version": sys.version,
            "platform": sys.platform,
            "cpu_count": os.cpu_count(),
            "memory_total": psutil.virtual_memory().total,
            "cwd": str(Path.cwd()),
        }

    def run_basic_tests(self) -> bool:
        """Run basic functionality tests."""
        print("🧪 Running basic functionality tests...")

        test_file = self.tests_dir / "test_basic.py"
        if not test_file.exists():
            print(f"❌ Test file not found: {test_file}")
            return False

        cmd = [
            sys.executable, "-m", "pytest",
            str(test_file),
            "-v",
            "--tb=short",
            "--capture=no"
        ]

        start_time = time.time()
        result = subprocess.run(cmd, capture_output=True, text=True)
        duration = time.time() - start_time

        self.results["test_results"]["basic"] = {
            "passed": result.returncode == 0,
            "duration": duration,
            "stdout": result.stdout,
            "stderr": result.stderr,
        }

        if result.returncode == 0:
            print(f"✅ Basic tests passed ({duration:.2f}s)")
            return True
        else:
            print(f"❌ Basic tests failed ({duration:.2f}s)")
            if result.stderr:
                print(f"Error: {result.stderr}")
            else:
                # Show stdout for pytest output which contains test details
                if result.stdout:
                    print(f"Test output: {result.stdout}")
                else:
                    print("No error details available")
            return False

    def run_integration_tests(self) -> bool:
        """Run integration tests."""
        print("🔗 Running integration tests...")

        test_file = self.tests_dir / "test_integration.py"
        if not test_file.exists():
            print(f"❌ Test file not found: {test_file}")
            return False

        cmd = [
            sys.executable, "-m", "pytest",
            str(test_file),
            "-v",
            "--tb=short",
            "-m", "not slow",  # Skip slow tests by default
        ]

        start_time = time.time()
        result = subprocess.run(cmd, capture_output=True, text=True)
        duration = time.time() - start_time

        self.results["test_results"]["integration"] = {
            "passed": result.returncode == 0,
            "duration": duration,
            "stdout": result.stdout,
            "stderr": result.stderr,
        }

        if result.returncode == 0:
            print(f"✅ Integration tests passed ({duration:.2f}s)")
            return True
        else:
            print(f"❌ Integration tests failed ({duration:.2f}s)")
            if result.stderr:
                print(f"Error: {result.stderr}")
            elif result.stdout:
                print(f"Test output: {result.stdout}")
            else:
                print("No error details available")
            return False

    def run_error_case_tests(self) -> bool:
        """Run comprehensive error case tests."""
        print("⚠️ Running error case tests...")

        test_file = self.tests_dir / "test_error_cases.py"
        if not test_file.exists():
            print(f"❌ Test file not found: {test_file}")
            return False

        cmd = [
            sys.executable, "-m", "pytest",
            str(test_file),
            "-v",
            "--tb=short",
        ]

        start_time = time.time()
        result = subprocess.run(cmd, capture_output=True, text=True)
        duration = time.time() - start_time

        self.results["test_results"]["error_cases"] = {
            "passed": result.returncode == 0,
            "duration": duration,
            "stdout": result.stdout,
            "stderr": result.stderr,
        }

        if result.returncode == 0:
            print(f"✅ Error case tests passed ({duration:.2f}s)")
            return True
        else:
            print(f"❌ Error case tests failed ({duration:.2f}s)")
            if result.stderr:
                print(f"Error: {result.stderr}")
            elif result.stdout:
                print(f"Test output: {result.stdout}")
            else:
                print("No error details available")
            return False

    # Performance tests removed - they contained async tests that were removed

    def run_compatibility_tests(self) -> bool:
        """Run cross-platform compatibility tests."""
        print("🌐 Running compatibility tests...")

        test_file = self.tests_dir / "test_compatibility.py"
        if not test_file.exists():
            print(f"❌ Test file not found: {test_file}")
            return False

        cmd = [
            sys.executable, "-m", "pytest",
            str(test_file),
            "-v",
            "--tb=short",
            "-s",
        ]

        start_time = time.time()
        result = subprocess.run(cmd, capture_output=True, text=True)
        duration = time.time() - start_time

        self.results["test_results"]["compatibility"] = {
            "passed": result.returncode == 0,
            "duration": duration,
            "stdout": result.stdout,
            "stderr": result.stderr,
        }

        if result.returncode == 0:
            print(f"✅ Compatibility tests passed ({duration:.2f}s)")
            return True
        else:
            print(f"❌ Compatibility tests failed ({duration:.2f}s)")
            if result.stderr:
                print(f"Error: {result.stderr}")
            elif result.stdout:
                print(f"Test output: {result.stdout}")
            else:
                print("No error details available")
            return False

    # Memory tests removed - they contained async tests that were removed


    def parse_performance_output(self, output: str) -> Dict[str, Any]:
        """Parse performance metrics from test output."""
        metrics = {}

        # Look for performance metrics in output
        lines = output.split('\n')
        for line in lines:
            if 'ops/sec' in line:
                # Parse lines like "keypair_generation: 123.45 ops/sec, avg: 8.12ms"
                parts = line.split(':')
                if len(parts) >= 2:
                    operation = parts[0].strip()
                    metrics_part = parts[1].strip()

                    # Extract ops/sec
                    if 'ops/sec' in metrics_part:
                        ops_sec = metrics_part.split('ops/sec')[0].strip()
                        try:
                            metrics[operation] = {"ops_per_second": float(ops_sec)}
                        except ValueError:
                            pass

        return metrics

    def parse_memory_output(self, output: str) -> Dict[str, Any]:
        """Parse memory metrics from test output."""
        metrics = {}

        lines = output.split('\n')
        for line in lines:
            if 'Memory Usage Summary' in line:
                # Start parsing memory summary
                continue
            elif 'MB' in line and (':' in line):
                # Parse lines like "  Peak: 45.23 MB"
                parts = line.split(':')
                if len(parts) == 2:
                    key = parts[0].strip()
                    value_str = parts[1].strip().replace('MB', '').strip()
                    try:
                        metrics[key] = float(value_str)
                    except ValueError:
                        pass

        return metrics

    def print_performance_summary(self, metrics: Dict[str, Any]):
        """Print performance test summary."""
        if not metrics:
            return

        print("\n📊 Performance Summary:")
        for operation, data in metrics.items():
            if isinstance(data, dict) and "ops_per_second" in data:
                ops_sec = data["ops_per_second"]
                print(f"  {operation}: {ops_sec:.2f} ops/sec")

    def print_memory_summary(self, metrics: Dict[str, Any]):
        """Print memory test summary."""
        if not metrics:
            return

        print("\n🧠 Memory Summary:")
        for key, value in metrics.items():
            if isinstance(value, (int, float)):
                print(f"  {key}: {value:.2f} MB")

    def generate_report(self, output_file: Optional[Path] = None):
        """Generate comprehensive test report."""
        if output_file is None:
            output_file = self.project_root / "test_report.json"

        # Add summary statistics
        self.results["summary"] = self.generate_summary()

        # Write report
        with open(output_file, 'w') as f:
            json.dump(self.results, f, indent=2, default=str)

        print(f"\n📋 Test report generated: {output_file}")

        # Print summary
        self.print_summary()

    def generate_summary(self) -> Dict[str, Any]:
        """Generate test summary statistics."""
        test_results = self.results.get("test_results", {})

        total_tests = len(test_results)
        passed_tests = sum(1 for result in test_results.values() if result.get("passed", False))

        total_duration = sum(result.get("duration", 0) for result in test_results.values())

        coverage_passed = self.results.get("coverage_results", {}).get("passed", False)

        return {
            "total_test_suites": total_tests,
            "passed_test_suites": passed_tests,
            "failed_test_suites": total_tests - passed_tests,
            "total_duration": total_duration,
            "coverage_analysis_passed": coverage_passed,
            "overall_success": passed_tests == total_tests,  # Only require core test suites to pass
        }

    def print_summary(self):
        """Print test execution summary."""
        summary = self.results.get("summary", {})

        print(f"\n{'='*50}")
        print("🎯 TEST EXECUTION SUMMARY")
        print(f"{'='*50}")

        total_suites = summary.get("total_test_suites", 0)
        passed_suites = summary.get("passed_test_suites", 0)
        failed_suites = summary.get("failed_test_suites", 0)

        print(f"Test Suites: {passed_suites}/{total_suites} passed")
        if failed_suites > 0:
            print(f"❌ {failed_suites} test suite(s) failed")
        else:
            print("✅ All test suites passed")

        print(f"Total Duration: {summary.get('total_duration', 0):.2f}s")

        # Performance and memory tests were removed (contained async tests)

        # Coverage info
        coverage_data = self.results.get("coverage_results", {}).get("coverage_data")
        if coverage_data:
            total_coverage = coverage_data.get("totals", {}).get("percent_covered", 0)
            print(f"Test Coverage: {total_coverage:.1f}%")

        # Overall status
        overall_success = summary.get("overall_success", False)
        print(f"\nOverall Status: {'✅ SUCCESS' if overall_success else '❌ FAILURE'}")
        print(f"{'='*50}")


def main():
    parser = argparse.ArgumentParser(description="Run Rialo Python CDK test suite")
    parser.add_argument("--basic", action="store_true", help="Run only basic tests")
    parser.add_argument("--integration", action="store_true", help="Run only integration tests")
    parser.add_argument("--compatibility", action="store_true", help="Run only compatibility tests")
    parser.add_argument("--error-cases", action="store_true", help="Run only error case tests")
    parser.add_argument("--coverage", action="store_true", help="Run coverage analysis")
    parser.add_argument("--all", action="store_true", help="Run all tests (default)")
    parser.add_argument("--report", type=str, help="Output report file path")
    parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")

    args = parser.parse_args()

    # Determine project root
    project_root = Path(__file__).parent
    if not (project_root / "tests").exists():
        print("❌ Tests directory not found")
        sys.exit(1)

    runner = TestRunner(project_root)

    print("🚀 Starting Rialo Python CDK Test Suite")
    print(f"📁 Project root: {project_root}")
    print(f"🐍 Python version: {sys.version.split()[0]}")
    print(f"💻 Platform: {sys.platform}")
    print()

    success = True

    # Determine which tests to run (performance and memory removed)
    run_all = args.all or not any([
        args.basic, args.integration, args.compatibility, args.error_cases, args.coverage
    ])

    if run_all or args.basic:
        if not runner.run_basic_tests():
            success = False

    if run_all or args.error_cases:
        if not runner.run_error_case_tests():
            success = False

    if run_all or args.compatibility:
        if not runner.run_compatibility_tests():
            success = False

    if run_all or args.integration:
        if not runner.run_integration_tests():
            success = False

    # Generate report
    report_file = Path(args.report) if args.report else None
    runner.generate_report(report_file)

    # Exit with appropriate code
    sys.exit(0 if success else 1)


if __name__ == "__main__":
    main()
