"""
Encryption utilities for sensitive data (e.g., API keys).
Uses Fernet symmetric encryption from cryptography library.
"""

import base64
import logging
import os

from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

logger = logging.getLogger(__name__)

# Get encryption key from environment
ENCRYPTION_KEY = os.getenv("ENCRYPTION_KEY")

# Fallback: Generate key from a password (NOT recommended for production)
# This is only for development/testing
FALLBACK_PASSWORD = os.getenv("ENCRYPTION_PASSWORD", "default-dev-password-change-in-production")


def _get_fernet_key() -> bytes:
    """
    Get or generate Fernet encryption key.
    Priority: ENCRYPTION_KEY env var > generated from password (dev only)
    """
    if ENCRYPTION_KEY:
        try:
            # Try to use as-is (should be base64-encoded 32-byte key)
            return ENCRYPTION_KEY.encode()
        except Exception:
            # If not valid, try to decode as base64
            try:
                return base64.urlsafe_b64decode(ENCRYPTION_KEY)
            except Exception:
                logger.warning("Invalid ENCRYPTION_KEY format, using fallback")
    
    # Fallback: Generate from password (dev only - NOT secure for production)
    logger.warning(
        "⚠️  ENCRYPTION_KEY not set. Using password-based key derivation (NOT secure for production!)"
    )
    salt = b"cucu_note_salt"  # Fixed salt for dev (should be random in production)
    kdf = PBKDF2HMAC(
        algorithm=hashes.SHA256(),
        length=32,
        salt=salt,
        iterations=100000,
    )
    key = base64.urlsafe_b64encode(kdf.derive(FALLBACK_PASSWORD.encode()))
    return key


def encrypt_api_key(api_key: str) -> str:
    """
    Encrypt an API key using Fernet symmetric encryption.
    
    Args:
        api_key: Plain text API key to encrypt
        
    Returns:
        Encrypted API key as base64 string
        
    Raises:
        ValueError: If api_key is empty
        RuntimeError: If encryption fails
    """
    if not api_key or not api_key.strip():
        raise ValueError("API key cannot be empty")
    
    try:
        fernet = Fernet(_get_fernet_key())
        encrypted = fernet.encrypt(api_key.encode())
        return encrypted.decode()
    except Exception as e:
        logger.error(f"Failed to encrypt API key: {e}")
        raise RuntimeError("Failed to encrypt API key") from e


def decrypt_api_key(encrypted_key: str) -> str:
    """
    Decrypt an encrypted API key.
    
    Args:
        encrypted_key: Encrypted API key (base64 string)
        
    Returns:
        Decrypted plain text API key
        
    Raises:
        ValueError: If encrypted_key is empty
        RuntimeError: If decryption fails (wrong key, corrupted data, etc.)
    """
    if not encrypted_key or not encrypted_key.strip():
        raise ValueError("Encrypted key cannot be empty")
    
    try:
        fernet = Fernet(_get_fernet_key())
        decrypted = fernet.decrypt(encrypted_key.encode())
        return decrypted.decode()
    except Exception as e:
        logger.error(f"Failed to decrypt API key: {e}")
        raise RuntimeError("Failed to decrypt API key. Key may be corrupted or encryption key changed.") from e


def mask_api_key(api_key: str) -> str:
    """
    Mask an API key for display (show only first 7 chars and last 4 chars).
    
    Args:
        api_key: API key to mask
        
    Returns:
        Masked API key (e.g., "sk-...xxxx")
    """
    if not api_key or len(api_key) < 11:
        return "sk-...xxxx"
    
    return f"{api_key[:7]}...{api_key[-4:]}"


def validate_openai_key_format(api_key: str) -> bool:
    """
    Validate OpenAI API key format.
    
    Args:
        api_key: API key to validate
        
    Returns:
        True if format is valid, False otherwise
    """
    if not api_key or not api_key.strip():
        return False
    
    # OpenAI keys typically start with "sk-" and are ~51 characters
    key = api_key.strip()
    if not key.startswith("sk-"):
        return False
    
    if len(key) < 20 or len(key) > 100:  # Reasonable range
        return False
    
    return True

