"""
Security module for Toxo - handles API key management, encryption, and secure storage.
"""

import os
from typing import Optional, Dict, Any
import base64
import getpass
import json
from pathlib import Path

from ..utils.logger import get_logger
from ..utils.exceptions import SecurityError

try:
    import keyring  # type: ignore
    KEYRING_AVAILABLE = True
except Exception:
    keyring = None
    KEYRING_AVAILABLE = False

try:
    from cryptography.fernet import Fernet  # type: ignore
    from cryptography.hazmat.primitives import hashes  # type: ignore
    from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC  # type: ignore
    CRYPTO_AVAILABLE = True
except Exception:
    Fernet = None
    hashes = None
    PBKDF2HMAC = None
    CRYPTO_AVAILABLE = False


class SecurityManager:
    """
    Manages API keys, encryption, and secure storage for Toxo.
    """
    
    def __init__(self, app_name: str = "toxo"):
        self.app_name = app_name
        self.logger = get_logger(__name__)
        self._cipher_suite = None
        self._setup_encryption()
        
    def _setup_encryption(self):
        """Setup encryption for sensitive data."""
        try:
            if not KEYRING_AVAILABLE or not CRYPTO_AVAILABLE:
                self._cipher_suite = None
                return

            # Try to get existing key from keyring
            key_data = keyring.get_password(self.app_name, "encryption_key")
            
            if key_data:
                self._cipher_suite = Fernet(key_data.encode())
            else:
                # Generate new key
                key = Fernet.generate_key()
                keyring.set_password(self.app_name, "encryption_key", key.decode())
                self._cipher_suite = Fernet(key)
                
            self.logger.info("Encryption setup completed")
            
        except Exception as e:
            self.logger.warning(f"Encryption setup failed: {e}")
            self._cipher_suite = None
    
    def get_api_key(self, service: str, user_provided_key: Optional[str] = None) -> str:
        """
        Get API key with preference: user_provided > environment > keyring > default
        
        Args:
            service: Service name (e.g., 'gemini', 'openai')
            user_provided_key: User-provided API key
            
        Returns:
            API key to use
        """
        # 1. Use user-provided key if available
        if user_provided_key:
            self.logger.info(f"Using user-provided API key for {service} (layer-specific)")
            return user_provided_key
        
        # 2. Check environment variables
        env_key = os.getenv(f"{service.upper()}_API_KEY")
        if env_key:
            self.logger.info(f"Using environment API key for {service}")
            return env_key
        
        # 3. Check keyring (secure storage)
        if KEYRING_AVAILABLE and keyring is not None:
            try:
                stored_key = keyring.get_password(self.app_name, f"{service}_api_key")
                if stored_key:
                    self.logger.info(f"Using stored API key for {service}")
                    return stored_key
            except Exception as e:
                self.logger.warning(f"Failed to retrieve stored key for {service}: {e}")
        
        raise SecurityError(f"No API key found for service: {service}")
    
    def store_api_key(self, service: str, api_key: str) -> bool:
        """
        Securely store an API key.
        
        Args:
            service: Service name
            api_key: API key to store
            
        Returns:
            True if successful
        """
        if not KEYRING_AVAILABLE or keyring is None:
            self.logger.warning("Keyring not available; cannot store API key securely")
            return False
        try:
            keyring.set_password(self.app_name, f"{service}_api_key", api_key)
            self.logger.info(f"API key stored securely for {service}")
            return True
        except Exception as e:
            self.logger.error(f"Failed to store API key for {service}: {e}")
            return False
    
    def prompt_for_api_key(self, service: str) -> str:
        """
        Prompt user for API key interactively.
        
        Args:
            service: Service name
            
        Returns:
            API key from user input
        """
        print(f"\n🔑 API Key Setup for {service.title()}")
        print(f"Please provide your {service.title()} API key.")
        print("This will be stored securely and used for all requests.")
        
        api_key = getpass.getpass(f"Enter {service.title()} API key: ")
        
        if not api_key:
            raise SecurityError(f"No API key provided for {service}")
        
        # Store the key securely
        if self.store_api_key(service, api_key):
            print(f"✅ API key stored securely for {service}")
        else:
            print(f"⚠️  Failed to store API key securely, will use for this session only")
        
        return api_key
    
    def encrypt_data(self, data: str) -> str:
        """
        Encrypt sensitive data.
        
        Args:
            data: Data to encrypt
            
        Returns:
            Encrypted data as base64 string
        """
        if not self._cipher_suite:
            self.logger.warning("Encryption not available, returning data as-is")
            return data
        
        try:
            encrypted_data = self._cipher_suite.encrypt(data.encode())
            return base64.b64encode(encrypted_data).decode()
        except Exception as e:
            self.logger.error(f"Encryption failed: {e}")
            return data
    
    def decrypt_data(self, encrypted_data: str) -> str:
        """
        Decrypt sensitive data.
        
        Args:
            encrypted_data: Encrypted data as base64 string
            
        Returns:
            Decrypted data
        """
        if not self._cipher_suite:
            self.logger.warning("Encryption not available, returning data as-is")
            return encrypted_data
        
        try:
            encrypted_bytes = base64.b64decode(encrypted_data.encode())
            decrypted_data = self._cipher_suite.decrypt(encrypted_bytes)
            return decrypted_data.decode()
        except Exception as e:
            self.logger.error(f"Decryption failed: {e}")
            return encrypted_data
    
    def secure_config_file(self, config_path: Path) -> Dict[str, Any]:
        """
        Load and decrypt configuration file.
        
        Args:
            config_path: Path to config file
            
        Returns:
            Decrypted configuration data
        """
        if not config_path.exists():
            return {}
        
        try:
            with open(config_path, 'r') as f:
                config_data = json.load(f)
            
            # Decrypt sensitive fields
            if 'api_keys' in config_data:
                for service, encrypted_key in config_data['api_keys'].items():
                    config_data['api_keys'][service] = self.decrypt_data(encrypted_key)
            
            return config_data
            
        except Exception as e:
            self.logger.error(f"Failed to load secure config: {e}")
            return {}
    
    def save_secure_config(self, config_data: Dict[str, Any], config_path: Path) -> bool:
        """
        Save configuration with encrypted sensitive data.
        
        Args:
            config_data: Configuration data
            config_path: Path to save config
            
        Returns:
            True if successful
        """
        try:
            # Encrypt sensitive fields
            secure_config = config_data.copy()
            if 'api_keys' in secure_config:
                for service, api_key in secure_config['api_keys'].items():
                    secure_config['api_keys'][service] = self.encrypt_data(api_key)
            
            # Save to file
            config_path.parent.mkdir(parents=True, exist_ok=True)
            with open(config_path, 'w') as f:
                json.dump(secure_config, f, indent=2)
            
            return True
            
        except Exception as e:
            self.logger.error(f"Failed to save secure config: {e}")
            return False
    
    def validate_api_key(self, service: str, api_key: str) -> bool:
        """
        Validate API key format.
        
        Args:
            service: Service name
            api_key: API key to validate
            
        Returns:
            True if valid format
        """
        validation_rules = {
            "gemini": lambda k: k.startswith("AIza") and len(k) > 30,
            "openai": lambda k: k.startswith("sk-") and len(k) > 40,
        }
        
        if service not in validation_rules:
            return True  # No validation rule, assume valid
        
        return validation_rules[service](api_key)
    
    def get_user_api_key_interactively(self, service: str) -> str:
        """
        Get API key from user with interactive prompts and validation.
        
        Args:
            service: Service name
            
        Returns:
            Validated API key
        """
        print(f"\n{'='*60}")
        print(f"🔐 TOXO API KEY CONFIGURATION")
        print(f"{'='*60}")
        print(f"Service: {service.title()}")
        print(f"")
        print(f"To use {service.title()} API, you need to provide your API key.")
        print(f"Your key will be stored securely and encrypted locally.")
        print(f"")
        
        # Check if we have environment variable
        env_key = os.getenv(f"{service.upper()}_API_KEY")
        if env_key:
            print(f"✅ Found {service.title()} API key in environment variable")
            return env_key
        
        # Check if we have stored key
        try:
            stored_key = keyring.get_password(self.app_name, f"{service}_api_key")
            if stored_key:
                response = input(f"Found stored {service.title()} API key. Use it? (y/n): ").strip().lower()
                if response in ['y', 'yes', '']:
                    return stored_key
        except:
            pass
        
        # Prompt for new key
        while True:
            print(f"\nOptions:")
            print(f"1. Enter your {service.title()} API key")
            print(f"2. Exit")
            
            choice = input("Choose option (1-2): ").strip()
            
            if choice == "1":
                api_key = getpass.getpass(f"Enter {service.title()} API key: ")
                if not api_key:
                    print("❌ No API key provided")
                    continue
                
                if not self.validate_api_key(service, api_key):
                    print(f"❌ Invalid {service.title()} API key format")
                    continue
                
                # Store the key
                if self.store_api_key(service, api_key):
                    print(f"✅ API key stored securely")
                
                return api_key
                
            elif choice == "2":
                raise SecurityError("User cancelled API key setup")
                
            else:
                print("❌ Invalid choice. Please enter 1 or 2.")


# Global security manager instance
_security_manager = None

def get_security_manager() -> SecurityManager:
    """Get global security manager instance."""
    global _security_manager
    if _security_manager is None:
        _security_manager = SecurityManager()
    return _security_manager 