#!/usr/bin/env python3
"""
Fenrir Release Validation Tool

Comprehensive validation suite for Fenrir releases, including syntax validation,
dependency checking, import testing, and basic functionality validation.

Usage:
    python3 tools/validate_release.py                    # Full validation
    python3 tools/validate_release.py --quick           # Skip slow tests
    python3 tools/validate_release.py --fix             # Auto-fix issues where possible
"""

import ast
import os
import sys
import argparse
import subprocess
import tempfile
import importlib.util
from pathlib import Path
import time


class ReleaseValidator:
    def __init__(self, verbose=True):
        self.verbose = verbose
        self.errors = []
        self.warnings = []
        self.fixes_applied = []
        self.tests_run = 0
        self.tests_passed = 0
        
    def log(self, message, level="INFO"):
        """Log a message with appropriate formatting."""
        if not self.verbose and level == "INFO":
            return
            
        colors = {
            "INFO": "\033[0;36m",     # Cyan
            "SUCCESS": "\033[0;32m",  # Green
            "WARNING": "\033[1;33m",  # Yellow
            "ERROR": "\033[0;31m",    # Red
            "HEADER": "\033[1;34m",   # Bold Blue
        }
        
        reset = "\033[0m"
        color = colors.get(level, "")
        
        if level == "HEADER":
            print(f"\n{color}{'='*60}")
            print(f"{message}")
            print(f"{'='*60}{reset}")
        else:
            symbol = {
                "SUCCESS": "✓",
                "ERROR": "✗", 
                "WARNING": "⚠",
                "INFO": "•"
            }.get(level, "•")
            
            print(f"{color}{symbol} {message}{reset}")
    
    def run_test(self, name, test_func, *args, **kwargs):
        """Run a test and track results."""
        self.tests_run += 1
        try:
            result = test_func(*args, **kwargs)
            if result:
                self.tests_passed += 1
                self.log(f"{name}: PASSED", "SUCCESS")
            else:
                self.log(f"{name}: FAILED", "ERROR")
            return result
        except Exception as e:
            self.log(f"{name}: ERROR - {e}", "ERROR")
            self.errors.append(f"{name}: {e}")
            return False
    
    def validate_python_syntax(self, directory, fix_mode=False):
        """Validate Python syntax across all files."""
        python_files = list(Path(directory).rglob("*.py"))
        # Filter out cache and build directories
        python_files = [f for f in python_files if not any(part.startswith(('__pycache__', '.git', 'build', 'dist')) for part in f.parts)]
        
        syntax_errors = []
        fixed_files = []
        
        for filepath in python_files:
            try:
                with open(filepath, 'r', encoding='utf-8') as f:
                    content = f.read()
                ast.parse(content, filename=str(filepath))
            except SyntaxError as e:
                syntax_errors.append((filepath, e))
                
                if fix_mode:
                    # Try to fix common f-string issues
                    fixed_content = self.fix_fstring_issues(content)
                    if fixed_content != content:
                        try:
                            ast.parse(fixed_content, filename=str(filepath))
                            # Fix worked, write it back
                            with open(filepath, 'w', encoding='utf-8') as f:
                                f.write(fixed_content)
                            fixed_files.append(filepath)
                            syntax_errors.pop()  # Remove from errors
                        except SyntaxError:
                            pass  # Fix didn't work
            except Exception as e:
                syntax_errors.append((filepath, e))
        
        if fixed_files:
            self.fixes_applied.extend([f"Fixed f-string syntax in {f}" for f in fixed_files])
        
        if syntax_errors:
            for filepath, error in syntax_errors[:5]:  # Show only first 5
                if isinstance(error, SyntaxError):
                    self.errors.append(f"Syntax error in {filepath}:{error.lineno}: {error.msg}")
                else:
                    self.errors.append(f"Error in {filepath}: {error}")
            if len(syntax_errors) > 5:
                self.errors.append(f"... and {len(syntax_errors) - 5} more syntax errors")
        
        return len(syntax_errors) == 0
    
    def fix_fstring_issues(self, content):
        """Fix common f-string syntax issues."""
        lines = content.split('\n')
        
        for i, line in enumerate(lines):
            # Look for f-strings that span multiple lines incorrectly
            if ('f"' in line and line.count('"') % 2 == 1 and 
                i + 1 < len(lines) and lines[i + 1].strip()):
                
                next_line = lines[i + 1]
                # Common patterns to fix
                if (next_line.strip().endswith('}"') or 
                    'str(e)}' in next_line or
                    next_line.strip().startswith(('fenrirVersion.', 'self.'))):
                    
                    # Merge the lines properly
                    fixed_line = line.rstrip() + next_line.strip()
                    lines[i] = fixed_line
                    lines[i + 1] = ''
        
        return '\n'.join(line for line in lines if line.strip() or not line)
    
    def validate_dependencies(self):
        """Run the existing dependency checker."""
        try:
            result = subprocess.run([
                sys.executable, "check-dependencies.py"
            ], capture_output=True, text=True, timeout=30)
            
            if result.returncode == 0:
                return True
            else:
                self.errors.append(f"Dependency check failed: {result.stderr}")
                return False
        except subprocess.TimeoutExpired:
            self.errors.append("Dependency check timed out")
            return False
        except Exception as e:
            self.errors.append(f"Could not run dependency check: {e}")
            return False
    
    def validate_core_imports(self):
        """Test importing core Fenrir modules."""
        # Change to src directory for imports
        original_path = sys.path.copy()
        src_dir = Path.cwd() / "src"
        if src_dir.exists():
            sys.path.insert(0, str(src_dir))
        
        core_modules = [
            "fenrirscreenreader.core.fenrirManager",
            "fenrirscreenreader.core.commandManager",
            "fenrirscreenreader.core.eventManager",
            "fenrirscreenreader.core.screenManager",
            "fenrirscreenreader.core.inputManager",
            "fenrirscreenreader.core.outputManager",
        ]
        
        import_failures = []
        
        for module_name in core_modules:
            try:
                importlib.import_module(module_name)
            except ImportError as e:
                import_failures.append(f"{module_name}: {e}")
            except Exception as e:
                import_failures.append(f"{module_name}: Unexpected error: {e}")
        
        # Restore path
        sys.path = original_path
        
        if import_failures:
            self.errors.extend(import_failures)
            return False
        return True
    
    def validate_command_structure(self):
        """Validate command file structure and naming."""
        commands_dir = Path("src/fenrirscreenreader/commands")
        if not commands_dir.exists():
            self.errors.append("Commands directory not found")
            return False
        
        issues = []
        
        # Check command directories
        expected_dirs = ["commands", "onHeartBeat", "onKeyInput", "onCursorChange", 
                        "onScreenUpdate", "onScreenChanged", "vmenu-profiles"]
        
        for expected_dir in expected_dirs:
            if not (commands_dir / expected_dir).exists():
                issues.append(f"Missing expected directory: {expected_dir}")
        
        # Check for critical issues only (skip template files and base classes)
        for py_file in commands_dir.rglob("*.py"):
            if (py_file.name.startswith("__") or 
                "template" in py_file.name.lower() or
                "base" in py_file.name.lower()):
                continue
                
            try:
                with open(py_file, 'r', encoding='utf-8') as f:
                    content = f.read()
                
                # Critical structure checks only
                if "class command" not in content:
                    issues.append(f"{py_file}: Missing 'class command' definition")
                
                # Skip method checks for files that inherit from base classes
                if ("super().__init__" in content or 
                    "importlib.util" in content or
                    "_base.py" in content):
                    continue  # These inherit methods from base classes
                
                # Only check direct implementations
                # Special case: Application profile commands use load/unload instead of run
                if "onSwitchApplicationProfile" in str(py_file):
                    if "def load" not in content and "def unload" not in content:
                        issues.append(f"{py_file}: Missing 'load' or 'unload' method")
                else:
                    critical_methods = ["run"]  # Focus on the most critical method
                    for method in critical_methods:
                        if (f"def {method}" not in content and 
                            "super()" not in content):  # Skip if uses inheritance
                            issues.append(f"{py_file}: Missing '{method}' method")
                        
            except Exception as e:
                issues.append(f"{py_file}: Could not validate structure: {e}")
        
        # Only report critical issues, not template/base class warnings
        critical_issues = [issue for issue in issues if not any(skip in issue.lower() 
                          for skip in ["template", "base", "missing 'initialize'", "missing 'shutdown'"])]
        
        if critical_issues:
            self.warnings.extend(critical_issues[:5])  # Limit warnings
            if len(critical_issues) > 5:
                self.warnings.append(f"... and {len(critical_issues) - 5} more critical command structure issues")
        
        # Return success if no critical issues (warnings are acceptable)
        return len(critical_issues) == 0
    
    def validate_configuration_files(self):
        """Validate configuration file structure."""
        config_dir = Path("config")
        if not config_dir.exists():
            self.errors.append("Config directory not found")
            return False
        
        required_configs = [
            "settings/settings.conf",
            "keyboard/desktop.conf", 
            "punctuation/default.conf"
        ]
        
        missing_configs = []
        for config_file in required_configs:
            if not (config_dir / config_file).exists():
                missing_configs.append(config_file)
        
        if missing_configs:
            self.errors.extend([f"Missing config file: {f}" for f in missing_configs])
            return False
        
        return True
    
    def validate_installation_scripts(self):
        """Validate installation and setup scripts."""
        required_scripts = ["setup.py", "install.sh", "uninstall.sh"]
        missing_scripts = []
        
        for script in required_scripts:
            if not Path(script).exists():
                missing_scripts.append(script)
        
        if missing_scripts:
            self.warnings.extend([f"Missing installation script: {s}" for s in missing_scripts])
        
        # Check setup.py syntax if it exists
        if Path("setup.py").exists():
            try:
                with open("setup.py", 'r') as f:
                    content = f.read()
                ast.parse(content, filename="setup.py")
            except SyntaxError as e:
                self.errors.append(f"setup.py syntax error: {e}")
                return False
        
        return len(missing_scripts) == 0
    
    def validate_repository_cleanliness(self):
        """Check for cache files and other artifacts that shouldn't be in git."""
        # Check for Python cache files in git tracking
        try:
            result = subprocess.run([
                "git", "ls-files", "--cached"
            ], capture_output=True, text=True, timeout=10)
            
            if result.returncode == 0:
                tracked_files = result.stdout.strip().split('\n')
                cache_files = [f for f in tracked_files if '__pycache__' in f or f.endswith('.pyc')]
                
                if cache_files:
                    self.errors.extend([f"Python cache file tracked in git: {f}" for f in cache_files[:5]])
                    if len(cache_files) > 5:
                        self.errors.append(f"... and {len(cache_files) - 5} more cache files in git")
                    return False
                else:
                    return True
            else:
                self.warnings.append("Could not check git tracked files")
                return True
                
        except subprocess.TimeoutExpired:
            self.warnings.append("Git check timed out")
            return True
        except Exception as e:
            self.warnings.append(f"Could not check repository cleanliness: {e}")
            return True
    
    def generate_report(self):
        """Generate final validation report."""
        self.log("FENRIR RELEASE VALIDATION REPORT", "HEADER")
        
        # Test Summary
        success_rate = (self.tests_passed / self.tests_run * 100) if self.tests_run > 0 else 0
        self.log(f"Tests run: {self.tests_run}")
        self.log(f"Tests passed: {self.tests_passed}")
        self.log(f"Success rate: {success_rate:.1f}%")
        
        # Fixes Applied
        if self.fixes_applied:
            self.log("\nAUTO-FIXES APPLIED:", "HEADER")
            for fix in self.fixes_applied:
                self.log(fix, "SUCCESS")
        
        # Errors
        if self.errors:
            self.log(f"\nERRORS ({len(self.errors)}):", "HEADER")
            for error in self.errors:
                self.log(error, "ERROR")
        
        # Warnings  
        if self.warnings:
            self.log(f"\nWARNINGS ({len(self.warnings)}):", "HEADER")
            for warning in self.warnings:
                self.log(warning, "WARNING")
        
        # Final Status
        if not self.errors and success_rate >= 80:
            self.log("\n🎉 RELEASE VALIDATION PASSED", "SUCCESS")
            self.log("The codebase appears ready for release", "SUCCESS")
            return True
        elif not self.errors:
            self.log("\n⚠️ RELEASE VALIDATION PASSED WITH WARNINGS", "WARNING")
            self.log("Release is possible but issues should be addressed", "WARNING")
            return True
        else:
            self.log("\n❌ RELEASE VALIDATION FAILED", "ERROR")
            self.log("Critical issues must be fixed before release", "ERROR")
            return False


def main():
    parser = argparse.ArgumentParser(description='Comprehensive Fenrir release validation')
    parser.add_argument('--quick', action='store_true',
                       help='Skip slow tests (dependency checking)')
    parser.add_argument('--fix', action='store_true',
                       help='Attempt to fix issues automatically where possible')
    parser.add_argument('--quiet', action='store_true',
                       help='Reduce output verbosity')
    
    args = parser.parse_args()
    
    # Ensure we're in the project root
    if not Path("src/fenrirscreenreader").exists():
        print("Error: Must be run from Fenrir project root directory")
        sys.exit(1)
    
    validator = ReleaseValidator(verbose=not args.quiet)
    
    validator.log("FENRIR RELEASE VALIDATION STARTING", "HEADER")
    start_time = time.time()
    
    # Run validation tests
    validator.run_test(
        "Python syntax validation",
        validator.validate_python_syntax,
        "src/fenrirscreenreader",
        args.fix
    )
    
    if not args.quick:
        validator.run_test(
            "Dependency validation", 
            validator.validate_dependencies
        )
    
    validator.run_test(
        "Core module imports",
        validator.validate_core_imports
    )
    
    validator.run_test(
        "Command structure validation",
        validator.validate_command_structure
    )
    
    validator.run_test(
        "Configuration files validation",
        validator.validate_configuration_files
    )
    
    validator.run_test(
        "Installation scripts validation",
        validator.validate_installation_scripts
    )
    
    validator.run_test(
        "Repository cleanliness validation",
        validator.validate_repository_cleanliness
    )
    
    # Generate final report
    elapsed_time = time.time() - start_time
    validator.log(f"\nValidation completed in {elapsed_time:.1f} seconds")
    
    success = validator.generate_report()
    
    sys.exit(0 if success else 1)


if __name__ == '__main__':
    main()
