#!/usr/bin/env python
"""
System Validation Script for Traffic Monitoring Application

This script performs a comprehensive check of the system components,
dependencies, and configuration to ensure the application is properly set up.
It validates:
1. Required Python packages
2. Model files existence and format
3. Configuration file correctness
4. UI components
5. Controller functionality
6. Hardware compatibility (GPU/OpenVINO)
7. Camera accessibility

Usage:
    python validate_system.py [--fix] [--verbose]
    
Options:
    --fix       Attempt to fix common issues (install packages, download models)
    --verbose   Show detailed output for all checks
"""

import os
import sys
import json
import platform
import importlib
import subprocess
import argparse
import traceback
from pathlib import Path
from typing import Dict, List, Tuple, Any, Optional

# Add parent directory to path to ensure imports work
sys.path.append(os.path.dirname(os.path.abspath(__file__)))

# Colors for terminal output
class Colors:
    HEADER = '\033[95m'
    BLUE = '\033[94m'
    GREEN = '\033[92m'
    YELLOW = '\033[93m'
    RED = '\033[91m'
    END = '\033[0m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'

# Define required packages with minimum versions
REQUIRED_PACKAGES = {
    'PySide6': '6.0.0',
    'opencv-python': '4.5.0',
    'numpy': '1.20.0',
    'openvino': '2021.4.0',
    'PyYAML': '5.4.0',
    'Pillow': '8.0.0',
    'matplotlib': '3.3.0',
    'pandas': '1.2.0',
    'torch': '1.8.0',
    'torchvision': '0.9.0',
}

# Define required model files
REQUIRED_MODELS = [
    'mobilenetv2.bin',
    'mobilenetv2.xml',
    'yolo11n.bin',
    'yolo11n.xml',
    'yolo11x.bin',
    'yolo11x.xml',
]

def print_header(message: str) -> None:
    """Print a formatted header message"""
    print(f"\n{Colors.HEADER}{Colors.BOLD}{'='*80}{Colors.END}")
    print(f"{Colors.HEADER}{Colors.BOLD} {message} {Colors.END}")
    print(f"{Colors.HEADER}{Colors.BOLD}{'='*80}{Colors.END}\n")

def print_result(test_name: str, status: bool, message: str = "") -> None:
    """Print a test result with appropriate formatting"""
    status_text = f"{Colors.GREEN}✓ PASS{Colors.END}" if status else f"{Colors.RED}✗ FAIL{Colors.END}"
    print(f"{test_name:<40} {status_text:<15} {message}")

def get_package_version(package_name: str) -> Optional[str]:
    """Get installed version of a package"""
    # Try using importlib.metadata first (Python 3.8+)
    try:
        # Try importlib.metadata (Python 3.8+)
        try:
            import importlib.metadata
            return importlib.metadata.version(package_name)
        except (ImportError, AttributeError):
            # Fallback for Python < 3.8
            try:
                import pkg_resources
                return pkg_resources.get_distribution(package_name).version
            except ImportError:
                # pkg_resources not available
                pass
            except Exception as e:
                # Other pkg_resources error
                pass
                # Distribution not found or other pkg_resources error
                pass
    except Exception:
        # Continue with other methods if any exception occurs
        pass
    
    # Try to import the package and check __version__
    try:
        pkg = importlib.import_module(package_name)
        if hasattr(pkg, "__version__"):
            return pkg.__version__
    except (ImportError, AttributeError):
        pass
    
    # Try pip list as a fallback for getting version info
    try:
        result = subprocess.run(
            [sys.executable, "-m", "pip", "show", package_name], 
            capture_output=True, 
            text=True
        )
        if result.returncode == 0:
            for line in result.stdout.split('\n'):
                if line.lower().startswith('version:'):
                    return line.split(':', 1)[1].strip()
    except Exception:
        pass
    
    # If we got here, we couldn't determine the version
    return None
    
def compare_versions(current: str, required: str) -> bool:
    """Compare two version strings"""
    if current is None:
        return False
    
    # Simple version comparison for now - can be enhanced with packaging.version
    current_parts = [int(x) for x in current.split('.')]
    required_parts = [int(x) for x in required.split('.')]
    
    # Pad with zeros to ensure equal length
    while len(current_parts) < len(required_parts):
        current_parts.append(0)
    while len(required_parts) < len(current_parts):
        required_parts.append(0)
    
    # Compare each part
    for c, r in zip(current_parts, required_parts):
        if c > r:
            return True
        if c < r:
            return False
    
    # Equal versions
    return True

def check_packages(fix: bool = False, verbose: bool = False) -> Tuple[bool, List[str]]:
    """Check if all required packages are installed with correct versions"""
    print_header("Checking Required Packages")
    all_passed = True
    missing_packages = []
    
    for package, min_version in REQUIRED_PACKAGES.items():
        current_version = get_package_version(package)
        if current_version is None:
            print_result(package, False, "Not installed")
            all_passed = False
            missing_packages.append(package)
        elif not compare_versions(current_version, min_version):
            print_result(package, False, f"Version {current_version} < required {min_version}")
            all_passed = False
            missing_packages.append(f"{package}=={min_version}")
        else:
            print_result(package, True, f"Version {current_version}")
    
    # Try to fix missing packages if requested
    if fix and missing_packages:
        print(f"\n{Colors.YELLOW}Attempting to install missing packages...{Colors.END}")
        try:
            cmd = [sys.executable, "-m", "pip", "install"] + missing_packages
            if verbose:
                print(f"Running: {' '.join(cmd)}")
            subprocess.check_call(cmd)
            print(f"{Colors.GREEN}Package installation complete.{Colors.END}")
            # Re-check packages after installation
            return check_packages(fix=False, verbose=verbose)
        except subprocess.CalledProcessError:
            print(f"{Colors.RED}Failed to install packages!{Colors.END}")
    
    return all_passed, missing_packages

def check_models(base_dir: Path, fix: bool = False, verbose: bool = False) -> Tuple[bool, List[str]]:
    """Check if all required model files exist"""
    print_header("Checking Model Files")
    all_passed = True
    missing_models = []
    
    # Check for models in different possible locations
    search_dirs = [
        base_dir,
        base_dir / "openvino_models",
        base_dir / "models",
        base_dir.parent / "openvino_models",
        base_dir.parent / "models"
    ]
    
    # Add specific model subdirectories that we know about
    additional_dirs = []
    for directory in search_dirs:
        if directory.exists():
            # Check for yolo11x_openvino_model subdirectory
            yolo11x_dir = directory / "yolo11x_openvino_model"
            if yolo11x_dir.exists():
                additional_dirs.append(yolo11x_dir)
            
            # Check for yolo11n_openvino_model subdirectory
            yolo11n_dir = directory / "yolo11n_openvino_model"
            if yolo11n_dir.exists():
                additional_dirs.append(yolo11n_dir)
                
            # Add all direct subdirectories of models directory
            if directory.name == "models" and directory.exists():
                for subdir in directory.iterdir():
                    if subdir.is_dir():
                        additional_dirs.append(subdir)
    
    # Add the additional directories to our search paths
    search_dirs.extend(additional_dirs)
    
    for model_file in REQUIRED_MODELS:
        found = False
        found_path = None
        for directory in search_dirs:
            if not directory.exists():
                continue
                
            model_path = directory / model_file
            if model_path.exists():
                found = True
                found_path = model_path
                print_result(model_file, True, f"Found in {directory}")
                break
        
        if not found:
            print_result(model_file, False, "Not found")
            all_passed = False
            missing_models.append(model_file)
        elif verbose:
            print(f"  Full path: {found_path}")
    
    # TODO: Implement model download functionality if fix=True
    if fix and missing_models:
        print(f"\n{Colors.YELLOW}Automatic model download not implemented yet.{Colors.END}")
        print(f"{Colors.YELLOW}Please download missing models manually.{Colors.END}")
    
    return all_passed, missing_models

def check_config(base_dir: Path, fix: bool = False, verbose: bool = False) -> Tuple[bool, List[str]]:
    """Check if configuration files exist and are valid"""
    print_header("Checking Configuration Files")
    all_passed = True
    issues = []
    
    # Check main config.json
    config_path = base_dir / "config.json"
    if not config_path.exists():
        print_result("config.json", False, "Not found")
        all_passed = False
        issues.append("Missing config.json")
        
        # Create default config if fix is enabled
        if fix:
            print(f"{Colors.YELLOW}Creating default config.json...{Colors.END}")
            default_config = {
                "video_sources": {
                    "default_camera_id": 0,
                    "default_video": ""
                },
                "detection_models": {
                    "yolo_model_xml": "openvino_models/yolo11n.xml",
                    "yolo_model_bin": "openvino_models/yolo11n.bin"
                },
                "detection_settings": {
                    "confidence_threshold": 0.5,
                    "use_gpu": True
                },
                "ui_settings": {
                    "theme": "dark",
                    "show_fps": True,
                    "default_tab": 0
                }
            }
            with open(config_path, 'w') as f:
                json.dump(default_config, f, indent=4)
            print(f"{Colors.GREEN}Created default config.json{Colors.END}")
    else:
        # Validate config.json
        try:
            with open(config_path, 'r') as f:
                config = json.load(f)
            
            # Check for required sections
            required_sections = ["video_sources", "detection_models", "detection_settings"]
            missing_sections = [s for s in required_sections if s not in config]
            
            if missing_sections:
                print_result("config.json structure", False, f"Missing sections: {', '.join(missing_sections)}")
                all_passed = False
                issues.append(f"Config missing sections: {', '.join(missing_sections)}")
                
                # Fix config if requested
                if fix:
                    print(f"{Colors.YELLOW}Adding missing sections to config.json...{Colors.END}")
                    for section in missing_sections:
                        if section == "video_sources":
                            config["video_sources"] = {"default_camera_id": 0, "default_video": ""}
                        elif section == "detection_models":
                            config["detection_models"] = {
                                "yolo_model_xml": "openvino_models/yolo11n.xml",
                                "yolo_model_bin": "openvino_models/yolo11n.bin"
                            }
                        elif section == "detection_settings":
                            config["detection_settings"] = {"confidence_threshold": 0.5, "use_gpu": True}
                    
                    with open(config_path, 'w') as f:
                        json.dump(config, f, indent=4)
                    print(f"{Colors.GREEN}Updated config.json with missing sections{Colors.END}")
            else:
                print_result("config.json structure", True, "All required sections present")
                
            # Check model paths in config
            model_xml = config.get("detection_models", {}).get("yolo_model_xml", "")
            model_bin = config.get("detection_models", {}).get("yolo_model_bin", "")
            
            if not (base_dir / model_xml).exists() and model_xml:
                print_result("Model path in config", False, f"Model XML not found at {model_xml}")
                all_passed = False
                issues.append(f"Invalid model path: {model_xml}")
            else:
                print_result("Model XML path", True, f"{model_xml}")
                
            if not (base_dir / model_bin).exists() and model_bin:
                print_result("Model path in config", False, f"Model BIN not found at {model_bin}")
                all_passed = False
                issues.append(f"Invalid model path: {model_bin}")
            else:
                print_result("Model BIN path", True, f"{model_bin}")
                
        except json.JSONDecodeError:
            print_result("config.json", False, "Invalid JSON format")
            all_passed = False
            issues.append("Invalid JSON in config.json")
            
            if fix:
                print(f"{Colors.YELLOW}Backing up and creating new config.json...{Colors.END}")
                # Backup invalid config
                os.rename(config_path, config_path.with_suffix('.json.bak'))
                # Create new default config
                default_config = {
                    "video_sources": {"default_camera_id": 0, "default_video": ""},
                    "detection_models": {
                        "yolo_model_xml": "openvino_models/yolo11n.xml",
                        "yolo_model_bin": "openvino_models/yolo11n.bin"
                    },
                    "detection_settings": {"confidence_threshold": 0.5, "use_gpu": True},
                    "ui_settings": {"theme": "dark", "show_fps": True, "default_tab": 0}
                }
                with open(config_path, 'w') as f:
                    json.dump(default_config, f, indent=4)
                print(f"{Colors.GREEN}Created new default config.json{Colors.END}")
    
    # Check for camera configuration files in violations directory
    camera_config_dir = base_dir / "violations" / "checkpoints"
    if camera_config_dir.exists():
        has_camera_configs = any(f.endswith('.yaml') for f in os.listdir(camera_config_dir))
        print_result("Camera configurations", has_camera_configs, 
                    "Found camera config files" if has_camera_configs else "No camera config files found (not critical)")
    else:
        print_result("Camera configurations", True, "Camera config directory not found (not critical)")
    
    return all_passed, issues

def check_ui_components(base_dir: Path, verbose: bool = False) -> Tuple[bool, List[str]]:
    """Check if all UI components exist and can be imported"""
    print_header("Checking UI Components")
    all_passed = True
    issues = []
    
    # List of critical UI files to check
    ui_files = [
        "ui/main_window.py",
        "ui/fixed_live_tab.py", 
        "ui/analytics_tab.py",
        "ui/violations_tab.py",
        "ui/export_tab.py",
        "ui/config_panel.py"
    ]
    
    # Check file existence
    for ui_file in ui_files:
        file_path = base_dir / ui_file
        if file_path.exists():
            print_result(ui_file, True, "File exists")
        else:
            print_result(ui_file, False, "File not found")
            all_passed = False
            issues.append(f"Missing UI file: {ui_file}")
    
    # Try importing main window
    if verbose:
        print("\nAttempting to import main window class...")
    try:
        sys.path.insert(0, str(base_dir))
        from ui.main_window import MainWindow
        print_result("MainWindow import", True, "Import successful")
    except ImportError as e:
        print_result("MainWindow import", False, f"Import failed: {str(e)}")
        all_passed = False
        issues.append(f"Failed to import MainWindow: {str(e)}")
    except Exception as e:
        print_result("MainWindow import", False, f"Error: {str(e)}")
        all_passed = False
        issues.append(f"Error importing MainWindow: {str(e)}")
        if verbose:
            traceback.print_exc()
    
    return all_passed, issues

def check_controllers(base_dir: Path, verbose: bool = False) -> Tuple[bool, List[str]]:
    """Check if all controllers exist and can be imported"""
    print_header("Checking Controllers")
    all_passed = True
    issues = []
    
    # List of controller files to check
    controller_files = [
        "controllers/video_controller_new.py",
        "controllers/analytics_controller.py",
        "controllers/model_manager.py",
        "controllers/performance_overlay.py"
    ]
    
    # Check file existence
    for controller_file in controller_files:
        file_path = base_dir / controller_file
        if file_path.exists():
            print_result(controller_file, True, "File exists")
        else:
            print_result(controller_file, False, "File not found")
            all_passed = False
            issues.append(f"Missing controller file: {controller_file}")
    
    # Try importing video controller
    if verbose:
        print("\nAttempting to import VideoController...")
    try:
        sys.path.insert(0, str(base_dir))
        from controllers.video_controller_new import VideoController
        print_result("VideoController import", True, "Import successful")
    except ImportError as e:
        print_result("VideoController import", False, f"Import failed: {str(e)}")
        all_passed = False
        issues.append(f"Failed to import VideoController: {str(e)}")
    except Exception as e:
        print_result("VideoController import", False, f"Error: {str(e)}")
        all_passed = False
        issues.append(f"Error importing VideoController: {str(e)}")
        if verbose:
            traceback.print_exc()
    
    return all_passed, issues

def check_hardware_compatibility(verbose: bool = False) -> Tuple[bool, Dict[str, Any]]:
    """Check hardware compatibility for OpenVINO and GPU acceleration"""
    print_header("Checking Hardware Compatibility")
    result = {
        "cpu_compatible": True,
        "gpu_available": False,
        "openvino_available": False,
        "system_info": {
            "os": platform.system(),
            "processor": platform.processor(),
            "python_version": platform.python_version(),
        }
    }
    
    # Print system information
    print(f"OS: {result['system_info']['os']}")
    print(f"Processor: {result['system_info']['processor']}")
    print(f"Python Version: {result['system_info']['python_version']}")
    
    # Check OpenVINO using subprocess to avoid import errors
    openvino_check_cmd = [sys.executable, "-c", "import openvino; print(openvino.__version__)"]
    try:
        openvino_output = subprocess.run(openvino_check_cmd, capture_output=True, text=True)
        if openvino_output.returncode == 0:
            openvino_version = openvino_output.stdout.strip()
            result["openvino_available"] = True
            result["openvino_version"] = openvino_version
            print_result("OpenVINO", True, f"Version {openvino_version}")
            
            # Try to get available devices
            device_check_cmd = [
                sys.executable, 
                "-c", 
                "from openvino.runtime import Core; ie = Core(); print(','.join(ie.available_devices))"
            ]
            device_output = subprocess.run(device_check_cmd, capture_output=True, text=True)
            if device_output.returncode == 0:
                devices = device_output.stdout.strip().split(',')
                result["available_devices"] = devices
                print(f"Available devices: {', '.join(devices)}")
                
                # Check for GPU
                if any("GPU" in device for device in devices):
                    result["gpu_available"] = True
                    print_result("GPU acceleration", True, "GPU device available for inference")
                else:
                    print_result("GPU acceleration", False, "No GPU device available for inference")
            else:
                print_result("Device query", False, "Could not query OpenVINO devices")
                if verbose and device_output.stderr:
                    print(f"Error: {device_output.stderr}")
        else:
            print_result("OpenVINO", False, "OpenVINO not installed or not working")
            if verbose and openvino_output.stderr:
                print(f"Error: {openvino_output.stderr}")
    except Exception as e:
        print_result("OpenVINO", False, f"Error checking OpenVINO: {str(e)}")
        result["openvino_available"] = False
        if verbose:
            traceback.print_exc()
    
    # Check for CUDA if torch is available using subprocess
    torch_check_cmd = [
        sys.executable, 
        "-c", 
        "import torch; print(f'{torch.__version__},{torch.cuda.is_available()}')"
    ]
    try:
        torch_output = subprocess.run(torch_check_cmd, capture_output=True, text=True)
        if torch_output.returncode == 0:
            torch_info = torch_output.stdout.strip().split(',')
            if len(torch_info) == 2:
                torch_version = torch_info[0]
                cuda_available = torch_info[1].lower() == 'true'
                result["torch_available"] = True
                result["torch_version"] = torch_version
                result["cuda_available"] = cuda_available
                
                if cuda_available:
                    # Get CUDA details
                    cuda_info_cmd = [
                        sys.executable, 
                        "-c", 
                        "import torch; print(f'{torch.version.cuda},{torch.cuda.device_count()},{torch.cuda.get_device_name(0) if torch.cuda.device_count() > 0 else \"Unknown\"}')"
                    ]
                    cuda_output = subprocess.run(cuda_info_cmd, capture_output=True, text=True)
                    if cuda_output.returncode == 0:
                        cuda_info = cuda_output.stdout.strip().split(',')
                        if len(cuda_info) == 3:
                            cuda_version = cuda_info[0]
                            device_count = cuda_info[1]
                            device_name = cuda_info[2]
                            
                            print_result("PyTorch CUDA", True, 
                                        f"CUDA {cuda_version}, {device_count} device(s): {device_name}")
                            result["cuda_version"] = cuda_version
                            result["cuda_device_count"] = int(device_count)
                            result["cuda_device_name"] = device_name
                            
                            # Update GPU availability
                            result["gpu_available"] = True
                    else:
                        print_result("PyTorch CUDA details", False, "Could not get CUDA details")
                        if verbose and cuda_output.stderr:
                            print(f"Error: {cuda_output.stderr}")
                else:
                    print_result("PyTorch CUDA", False, "CUDA not available")
        else:
            print_result("PyTorch", False, "PyTorch not installed")
            if verbose and torch_output.stderr:
                print(f"Error: {torch_output.stderr}")
    except Exception as e:
        print_result("PyTorch check", False, f"Error: {str(e)}")
        result["torch_available"] = False
        if verbose:
            traceback.print_exc()
    
    # Consider the system compatible if either OpenVINO is available or GPU acceleration is available
    return result["openvino_available"] or result["gpu_available"], result

def check_camera_access(verbose: bool = False) -> Tuple[bool, List[int]]:
    """Check if cameras are accessible"""
    print_header("Checking Camera Access")
    
    available_cameras = []
    camera_access = False
    
    # Use subprocess to run OpenCV check to avoid direct import errors
    cv2_check_script = """
import cv2
import json
import sys

def check_cameras(max_id=3):
    results = []
    for camera_id in range(max_id+1):
        cap = cv2.VideoCapture(camera_id)
        if cap.isOpened():
            ret, frame = cap.read()
            if ret:
                h, w = frame.shape[:2]
                results.append({
                    'id': camera_id,
                    'accessible': True,
                    'width': w, 
                    'height': h
                })
            else:
                results.append({
                    'id': camera_id,
                    'accessible': False
                })
            cap.release()
        else:
            results.append({
                'id': camera_id,
                'accessible': False
            })
    return results

# Only check additional cameras if verbose mode is enabled
max_id = 3 if len(sys.argv) > 1 and sys.argv[1] == 'verbose' else 0
results = check_cameras(max_id)
print(json.dumps(results))
"""
    
    try:
        # Execute the camera check script
        cmd = [sys.executable, "-c", cv2_check_script]
        if verbose:
            cmd.append("verbose")
        
        result = subprocess.run(cmd, capture_output=True, text=True)
        
        if result.returncode == 0:
            try:
                camera_results = json.loads(result.stdout)
                for camera in camera_results:
                    camera_id = camera.get('id', 0)
                    if camera.get('accessible', False):
                        camera_access = True
                        available_cameras.append(camera_id)
                        resolution = f"{camera.get('width', 'unknown')}x{camera.get('height', 'unknown')}"
                        print_result(f"Camera (ID: {camera_id})", True, f"Accessible, Resolution: {resolution}")
                    else:
                        print_result(f"Camera (ID: {camera_id})", False, "Not accessible")
            except json.JSONDecodeError:
                print_result("Camera check", False, "Error parsing camera results")
                if verbose:
                    print(f"Output: {result.stdout}")
        else:
            print_result("Camera access", False, "OpenCV not installed or error accessing cameras")
            if verbose and result.stderr:
                print(f"Error: {result.stderr}")
    except Exception as e:
        print_result("Camera check", False, f"Error: {str(e)}")
        if verbose:
            traceback.print_exc()
    
    return camera_access, available_cameras

def main():
    """Main function to run system validation"""
    parser = argparse.ArgumentParser(description="Validate Traffic Monitoring System")
    parser.add_argument("--fix", action="store_true", help="Attempt to fix issues")
    parser.add_argument("--verbose", action="store_true", help="Show detailed output")
    args = parser.parse_args()
    
    # Get base directory
    base_dir = Path(os.path.dirname(os.path.abspath(__file__)))
    
    print(f"\n{Colors.BOLD}Traffic Monitoring System Validation{Colors.END}")
    print(f"Base directory: {base_dir}")
    print(f"Python executable: {sys.executable}")
    
    # Run all checks
    packages_ok, missing_packages = check_packages(fix=args.fix, verbose=args.verbose)
    models_ok, missing_models = check_models(base_dir, fix=args.fix, verbose=args.verbose)
    config_ok, config_issues = check_config(base_dir, fix=args.fix, verbose=args.verbose)
    ui_ok, ui_issues = check_ui_components(base_dir, verbose=args.verbose)
    controllers_ok, controller_issues = check_controllers(base_dir, verbose=args.verbose)
    hw_ok, hw_info = check_hardware_compatibility(verbose=args.verbose)
    camera_ok, available_cameras = check_camera_access(verbose=args.verbose)
    
    # Print summary
    print_header("Validation Summary")
    print_result("Python Packages", packages_ok, f"{len(missing_packages)} issues" if missing_packages else "All required packages found")
    print_result("Model Files", models_ok, f"{len(missing_models)} issues" if missing_models else "All model files found")
    print_result("Configuration", config_ok, f"{len(config_issues)} issues" if config_issues else "Configuration valid")
    print_result("UI Components", ui_ok, f"{len(ui_issues)} issues" if ui_issues else "All UI components found")
    print_result("Controllers", controllers_ok, f"{len(controller_issues)} issues" if controller_issues else "All controllers found")
    print_result("Hardware Compatibility", hw_ok, "GPU or OpenVINO available" if hw_ok else "No GPU or OpenVINO")
    print_result("Camera Access", camera_ok, f"{len(available_cameras)} cameras available" if camera_ok else "No cameras accessible")
    
    # Overall result
    all_ok = packages_ok and models_ok and config_ok and ui_ok and controllers_ok and hw_ok
    
    print(f"\n{Colors.BOLD}Overall Result: {'SUCCESS' if all_ok else 'ISSUES FOUND'}{Colors.END}")
    if all_ok:
        print(f"{Colors.GREEN}The system is correctly set up and should run without issues.{Colors.END}")
    else:
        print(f"{Colors.YELLOW}Please fix the issues reported above before running the application.{Colors.END}")
        
        if missing_packages:
            print(f"\n{Colors.BOLD}Missing Packages:{Colors.END}")
            print(f"Run: {sys.executable} -m pip install " + " ".join(missing_packages))
        
        if missing_models:
            print(f"\n{Colors.BOLD}Missing Models:{Colors.END}")
            print("Download missing model files or check their paths in config.json")
    
    return 0 if all_ok else 1

if __name__ == "__main__":
    sys.exit(main())
