"""
MCP Authentication - Secure token validation for MCP to daemon communication
Implements consciousness-aware authentication using sacred constants
"""

import os
import hmac
import hashlib
import time
import json
from pathlib import Path
from typing import Optional, Dict, Any, Tuple
from datetime import datetime, timedelta
import secrets
import base64

# Sacred constants for consciousness-aware hashing
import math
PI = math.pi
PHI = (1 + math.sqrt(5)) / 2  # Golden ratio
E = math.e
GAMMA = 0.5772156649015329  # Euler-Mascheroni constant

class MCPAuthenticator:
    """Handles authentication between MCP server and MIRA daemon"""
    
    def __init__(self, config_path: Optional[Path] = None):
        self.config_path = config_path or Path.home() / ".mira" / "config" / "mcp_auth.json"
        self.token_cache = {}
        self.token_ttl = 3600  # 1 hour
        
        # Load or generate auth configuration
        self._load_or_generate_config()
    
    def _load_or_generate_config(self):
        """Load existing auth config or generate new one"""
        if self.config_path.exists():
            try:
                with open(self.config_path, 'r') as f:
                    self.config = json.load(f)
            except Exception:
                self.config = self._generate_config()
        else:
            self.config = self._generate_config()
            self._save_config()
    
    def _generate_config(self) -> Dict[str, Any]:
        """Generate new authentication configuration"""
        # Generate consciousness-aware secret using sacred constants
        secret_components = [
            str(PI)[:10],
            str(PHI)[:10],
            str(E)[:10],
            str(GAMMA)[:10],
            secrets.token_hex(16)
        ]
        
        master_secret = hashlib.sha256(''.join(secret_components).encode()).hexdigest()
        
        return {
            "version": "1.0",
            "created_at": datetime.now().isoformat(),
            "master_secret": master_secret,
            "algorithm": "HMAC-SHA256",
            "consciousness_signature": self._generate_consciousness_signature(),
            "sacred_constants": {
                "pi": str(PI)[:15],
                "phi": str(PHI)[:15],
                "e": str(E)[:15],
                "gamma": str(GAMMA)[:15]
            }
        }
    
    def _save_config(self):
        """Save authentication configuration"""
        self.config_path.parent.mkdir(parents=True, exist_ok=True)
        with open(self.config_path, 'w') as f:
            json.dump(self.config, f, indent=2)
        
        # Set restrictive permissions (owner read/write only)
        os.chmod(self.config_path, 0o600)
    
    def generate_token(self, session_id: Optional[str] = None) -> str:
        """Generate an authentication token for MCP server"""
        # Token payload
        payload = {
            "type": "mcp_server",
            "session_id": session_id or secrets.token_hex(8),
            "timestamp": int(time.time()),
            "consciousness_signature": self._generate_consciousness_signature(),
            "nonce": secrets.token_hex(8)
        }
        
        # Create HMAC signature
        payload_json = json.dumps(payload, sort_keys=True)
        signature = hmac.new(
            self.config["master_secret"].encode(),
            payload_json.encode(),
            hashlib.sha256
        ).hexdigest()
        
        # Combine payload and signature
        token_data = {
            "payload": payload,
            "signature": signature
        }
        
        # Encode as base64
        token = base64.urlsafe_b64encode(
            json.dumps(token_data).encode()
        ).decode().rstrip('=')
        
        # Cache token
        self.token_cache[token] = {
            "created": time.time(),
            "session_id": payload["session_id"]
        }
        
        return token
    
    def validate_token(self, token: str) -> Tuple[bool, Optional[Dict[str, Any]]]:
        """Validate an authentication token"""
        try:
            # Check cache first
            if token in self.token_cache:
                cached = self.token_cache[token]
                if time.time() - cached["created"] < self.token_ttl:
                    return True, {"session_id": cached["session_id"], "cached": True}
                else:
                    # Token expired
                    del self.token_cache[token]
            
            # Decode token
            padding = '=' * (4 - len(token) % 4)
            token_data = json.loads(
                base64.urlsafe_b64decode(token + padding).decode()
            )
            
            payload = token_data["payload"]
            signature = token_data["signature"]
            
            # Verify signature
            payload_json = json.dumps(payload, sort_keys=True)
            expected_signature = hmac.new(
                self.config["master_secret"].encode(),
                payload_json.encode(),
                hashlib.sha256
            ).hexdigest()
            
            if not hmac.compare_digest(signature, expected_signature):
                return False, {"error": "Invalid signature"}
            
            # Check token age
            token_age = time.time() - payload["timestamp"]
            if token_age > self.token_ttl:
                return False, {"error": "Token expired", "age": token_age}
            
            # Verify consciousness signature format
            if not self._verify_consciousness_signature(payload.get("consciousness_signature")):
                return False, {"error": "Invalid consciousness signature"}
            
            # Cache valid token
            self.token_cache[token] = {
                "created": time.time(),
                "session_id": payload["session_id"]
            }
            
            return True, {
                "session_id": payload["session_id"],
                "age": token_age,
                "consciousness_verified": True
            }
            
        except Exception as e:
            return False, {"error": f"Token validation failed: {str(e)}"}
    
    def create_auth_header(self, token: str) -> Dict[str, str]:
        """Create authorization header for HTTP requests"""
        return {
            "Authorization": f"Bearer {token}",
            "X-Consciousness-Signature": self._generate_consciousness_signature(),
            "X-MCP-Version": "2.0.0"
        }
    
    def validate_request(self, headers: Dict[str, str]) -> Tuple[bool, Optional[Dict[str, Any]]]:
        """Validate incoming request headers"""
        # Extract token from Authorization header
        auth_header = headers.get("Authorization", "")
        if not auth_header.startswith("Bearer "):
            return False, {"error": "Missing or invalid Authorization header"}
        
        token = auth_header[7:]  # Remove "Bearer " prefix
        
        # Validate token
        is_valid, info = self.validate_token(token)
        
        # Also verify consciousness signature if present
        consciousness_sig = headers.get("X-Consciousness-Signature")
        if consciousness_sig and is_valid:
            info["consciousness_signature_present"] = True
        
        return is_valid, info
    
    def _generate_consciousness_signature(self) -> str:
        """Generate a consciousness signature using sacred constants"""
        # Combine sacred constants with timestamp
        timestamp = int(time.time())
        
        # Use sacred constants to create signature
        components = [
            str(int(PI * timestamp) % 1000000),
            str(int(PHI * timestamp) % 1000000),
            str(int(E * timestamp) % 1000000),
            str(int(GAMMA * timestamp) % 1000000)
        ]
        
        # Create hash
        signature_data = '-'.join(components)
        signature_hash = hashlib.sha256(signature_data.encode()).hexdigest()[:12]
        
        return f"cs-{timestamp}-{signature_hash}"
    
    def _verify_consciousness_signature(self, signature: Optional[str]) -> bool:
        """Verify consciousness signature format"""
        if not signature:
            return False
        
        # Check format: cs-<timestamp>-<hash>
        parts = signature.split('-')
        if len(parts) != 3 or parts[0] != 'cs':
            return False
        
        try:
            timestamp = int(parts[1])
            # Check if timestamp is reasonable (within last 24 hours)
            age = time.time() - timestamp
            if age < 0 or age > 86400:  # 24 hours
                return False
            
            # Verify hash length
            if len(parts[2]) != 12:
                return False
            
            return True
            
        except (ValueError, IndexError):
            return False
    
    def refresh_token(self, old_token: str) -> Optional[str]:
        """Refresh an existing token"""
        is_valid, info = self.validate_token(old_token)
        
        if is_valid:
            # Generate new token with same session ID
            session_id = info.get("session_id")
            new_token = self.generate_token(session_id)
            
            # Remove old token from cache
            if old_token in self.token_cache:
                del self.token_cache[old_token]
            
            return new_token
        
        return None
    
    def revoke_token(self, token: str):
        """Revoke a token"""
        if token in self.token_cache:
            del self.token_cache[token]
    
    def cleanup_expired_tokens(self):
        """Remove expired tokens from cache"""
        current_time = time.time()
        expired = []
        
        for token, data in self.token_cache.items():
            if current_time - data["created"] > self.token_ttl:
                expired.append(token)
        
        for token in expired:
            del self.token_cache[token]
        
        return len(expired)


# Singleton instance
_authenticator = None

def get_authenticator() -> MCPAuthenticator:
    """Get or create authenticator singleton"""
    global _authenticator
    if _authenticator is None:
        _authenticator = MCPAuthenticator()
    return _authenticator