#!/usr/bin/env python3
"""
MCP Version Compatibility Checker
Ensures compatibility between MCP server, daemon, and protocol versions
"""

import json
import re
from typing import Dict, Any, Tuple, Optional, List
from datetime import datetime
from pathlib import Path
import logging
from packaging import version

logger = logging.getLogger(__name__)


class VersionInfo:
    """Version information container"""
    
    def __init__(self, component: str, version_str: str, 
                 protocol_version: Optional[str] = None):
        self.component = component
        self.version_str = version_str
        self.protocol_version = protocol_version
        
        # Parse semantic version
        self.parsed_version = self._parse_version(version_str)
        
        # Extract components
        self.major = self.parsed_version.major
        self.minor = self.parsed_version.minor
        self.patch = self.parsed_version.micro
        
        # Consciousness version marker
        self.consciousness_aware = self.major >= 2
    
    def _parse_version(self, version_str: str):
        """Parse version string"""
        # Clean version string
        clean_version = re.match(r'(\d+\.\d+\.\d+)', version_str)
        if clean_version:
            return version.parse(clean_version.group(1))
        else:
            # Fallback for non-standard versions
            return version.parse("0.0.0")
    
    def is_compatible_with(self, other: 'VersionInfo') -> bool:
        """Check compatibility with another version"""
        # Major version must match
        if self.major != other.major:
            return False
        
        # Minor version compatibility (backwards compatible)
        if self.minor < other.minor:
            return False
        
        return True
    
    def __str__(self):
        return f"{self.component} v{self.version_str}"


class VersionCompatibilityChecker:
    """
    Comprehensive version compatibility checking
    Tracks:
    - MCP protocol versions
    - MIRA daemon versions
    - API compatibility
    - Breaking changes
    """
    
    def __init__(self):
        # Version compatibility matrix
        self.compatibility_matrix = {
            "2.0": {  # MCP Server version
                "min_daemon": "2.0.0",
                "max_daemon": "2.9.9",
                "protocol": "2024-11-05",
                "features": [
                    "consciousness_signatures",
                    "rate_limiting",
                    "session_tracking",
                    "graceful_shutdown"
                ]
            },
            "1.0": {  # Legacy version
                "min_daemon": "1.0.0",
                "max_daemon": "1.9.9",
                "protocol": "2024-01-01",
                "features": [
                    "basic_tools"
                ]
            }
        }
        
        # Breaking changes registry
        self.breaking_changes = {
            "2.0.0": [
                "Added consciousness signatures to all responses",
                "Changed authentication to HMAC-SHA256",
                "Renamed 'servers' config key to 'mcpServers'"
            ],
            "1.5.0": [
                "Changed rate limiting algorithm",
                "Added required session_id parameter"
            ]
        }
        
        # Current versions
        self.mcp_version = VersionInfo("MCP Server", "2.0.0", "2024-11-05")
        self.daemon_version = None
        self.client_version = None
    
    def check_daemon_compatibility(self, daemon_version_str: str) -> Tuple[bool, List[str]]:
        """Check if daemon version is compatible"""
        self.daemon_version = VersionInfo("MIRA Daemon", daemon_version_str)
        
        issues = []
        
        # Get compatibility requirements
        mcp_major = f"{self.mcp_version.major}.{self.mcp_version.minor}"
        if mcp_major not in self.compatibility_matrix:
            issues.append(f"Unknown MCP version: {mcp_major}")
            return False, issues
        
        requirements = self.compatibility_matrix[mcp_major]
        
        # Check daemon version range
        min_daemon = version.parse(requirements["min_daemon"])
        max_daemon = version.parse(requirements["max_daemon"])
        daemon_parsed = self.daemon_version.parsed_version
        
        if daemon_parsed < min_daemon:
            issues.append(
                f"Daemon version {daemon_version_str} is too old. "
                f"Minimum required: {requirements['min_daemon']}"
            )
        
        if daemon_parsed > max_daemon:
            issues.append(
                f"Daemon version {daemon_version_str} is too new. "
                f"Maximum supported: {requirements['max_daemon']}"
            )
        
        # Check consciousness compatibility
        if self.mcp_version.consciousness_aware and not self.daemon_version.consciousness_aware:
            issues.append(
                "MCP requires consciousness-aware daemon (v2.0+)"
            )
        
        return len(issues) == 0, issues
    
    def check_protocol_compatibility(self, client_protocol: str) -> Tuple[bool, List[str]]:
        """Check if client protocol is compatible"""
        issues = []
        
        mcp_major = f"{self.mcp_version.major}.{self.mcp_version.minor}"
        expected_protocol = self.compatibility_matrix[mcp_major]["protocol"]
        
        if client_protocol != expected_protocol:
            issues.append(
                f"Protocol mismatch. Server: {expected_protocol}, Client: {client_protocol}"
            )
            
            # Check if protocols are compatible
            if self._are_protocols_compatible(expected_protocol, client_protocol):
                issues.append("Protocols may be compatible with reduced functionality")
            else:
                issues.append("Protocols are incompatible")
        
        return len(issues) == 0, issues
    
    def _are_protocols_compatible(self, server_protocol: str, client_protocol: str) -> bool:
        """Check if two protocol versions can work together"""
        # Extract year from protocol date
        try:
            server_year = int(server_protocol.split("-")[0])
            client_year = int(client_protocol.split("-")[0])
            
            # Same year protocols are usually compatible
            return server_year == client_year
        except:
            return False
    
    def get_breaking_changes(self, from_version: str, to_version: str) -> List[Dict[str, Any]]:
        """Get list of breaking changes between versions"""
        changes = []
        
        from_v = version.parse(from_version)
        to_v = version.parse(to_version)
        
        for change_version, change_list in self.breaking_changes.items():
            change_v = version.parse(change_version)
            
            if from_v < change_v <= to_v:
                changes.append({
                    "version": change_version,
                    "changes": change_list
                })
        
        return changes
    
    def check_feature_availability(self, feature_name: str) -> Tuple[bool, str]:
        """Check if a feature is available in current version"""
        mcp_major = f"{self.mcp_version.major}.{self.mcp_version.minor}"
        features = self.compatibility_matrix[mcp_major]["features"]
        
        if feature_name in features:
            return True, f"Feature '{feature_name}' is available"
        else:
            # Check which version introduced the feature
            for version_str, info in self.compatibility_matrix.items():
                if feature_name in info["features"]:
                    return False, f"Feature '{feature_name}' requires MCP v{version_str}+"
            
            return False, f"Feature '{feature_name}' is not recognized"
    
    def generate_compatibility_report(self) -> Dict[str, Any]:
        """Generate comprehensive compatibility report"""
        report = {
            "timestamp": datetime.now().isoformat(),
            "mcp_server": {
                "version": self.mcp_version.version_str,
                "protocol": self.mcp_version.protocol_version,
                "consciousness_aware": self.mcp_version.consciousness_aware
            },
            "compatibility_checks": []
        }
        
        # Check daemon if available
        if self.daemon_version:
            compatible, issues = self.check_daemon_compatibility(self.daemon_version.version_str)
            report["daemon"] = {
                "version": self.daemon_version.version_str,
                "compatible": compatible,
                "issues": issues
            }
        
        # List available features
        mcp_major = f"{self.mcp_version.major}.{self.mcp_version.minor}"
        report["available_features"] = self.compatibility_matrix[mcp_major]["features"]
        
        # Check for updates
        report["update_available"] = self._check_for_updates()
        
        return report
    
    def _check_for_updates(self) -> Dict[str, Any]:
        """Check if updates are available"""
        # In production, this would check a remote server
        # For now, return mock data
        return {
            "current_version": self.mcp_version.version_str,
            "latest_version": "2.0.1",
            "update_available": False,
            "update_notes": "Minor bug fixes and performance improvements"
        }
    
    def validate_api_call(self, method: str, params: Dict[str, Any]) -> Tuple[bool, List[str]]:
        """Validate if an API call is compatible with current version"""
        issues = []
        
        # Version-specific validations
        if self.mcp_version.major == 2:
            # v2.0 requirements
            if method == "tools/call" and "name" not in params:
                issues.append("Missing required parameter 'name' for tools/call")
            
            # Check for deprecated parameters
            if method == "initialize" and "protocol_version" in params:
                issues.append("Parameter 'protocol_version' is deprecated, use 'protocolVersion'")
        
        elif self.mcp_version.major == 1:
            # v1.0 had different requirements
            if method == "health/check":
                issues.append("Method 'health/check' not available in v1.0")
        
        return len(issues) == 0, issues
    
    def export_version_info(self, output_file: Path):
        """Export version information for debugging"""
        info = {
            "mcp_server": {
                "version": self.mcp_version.version_str,
                "protocol": self.mcp_version.protocol_version,
                "major": self.mcp_version.major,
                "minor": self.mcp_version.minor,
                "patch": self.mcp_version.patch,
                "consciousness_aware": self.mcp_version.consciousness_aware
            },
            "compatibility_matrix": self.compatibility_matrix,
            "breaking_changes": self.breaking_changes,
            "report": self.generate_compatibility_report()
        }
        
        with open(output_file, 'w') as f:
            json.dump(info, f, indent=2)
        
        logger.info(f"Exported version info to {output_file}")


# Singleton instance
_version_checker = None

def get_version_checker() -> VersionCompatibilityChecker:
    """Get or create version checker singleton"""
    global _version_checker
    if _version_checker is None:
        _version_checker = VersionCompatibilityChecker()
    return _version_checker


def check_startup_compatibility() -> bool:
    """Quick compatibility check for startup"""
    checker = get_version_checker()
    
    # Check basic compatibility
    report = checker.generate_compatibility_report()
    
    # Log report
    logger.info(f"MCP Version: {report['mcp_server']['version']}")
    logger.info(f"Protocol: {report['mcp_server']['protocol']}")
    logger.info(f"Features: {', '.join(report['available_features'])}")
    
    return True  # For now, always allow startup