import logging
from cryptography.fernet import Fernet, InvalidToken
from src.core.config import settings
from src.core.exceptions import InternalServerError

logger = logging.getLogger("credentials")


def _get_fernet() -> Fernet:
    key = settings.CREDENTIAL_ENCRYPTION_KEY
    if not key:
        raise RuntimeError(
            "CREDENTIAL_ENCRYPTION_KEY is not configured. "
            "Set it in your .env file. Generate a valid key with: "
            "python -c \"from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())\""
        )
    raw = key.encode() if isinstance(key, str) else key
    try:
        return Fernet(raw)
    except Exception:
        # Do NOT log the key value — only flag the misconfiguration.
        raise RuntimeError(
            "CREDENTIAL_ENCRYPTION_KEY is set but is not a valid Fernet key. "
            "Generate one with: python -c \"from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())\""
        )


def encrypt_credential(value: str) -> str:
    """Encrypt a credential value using Fernet (AES-128-CBC + HMAC-SHA256)."""
    f = _get_fernet()
    return f.encrypt(value.encode()).decode()


def decrypt_credential(encrypted_value: str) -> str:
    """Decrypt a Fernet-encrypted credential value.

    Raises InternalServerError (not a raw exception with ciphertext) if decryption
    fails, so that the encrypted value is never surfaced in error messages or logs.
    """
    f = _get_fernet()
    try:
        return f.decrypt(encrypted_value.encode()).decode()
    except InvalidToken:
        # Log only that decryption failed — never log the ciphertext or the key.
        logger.error("op=decrypt_credential result=invalid_token — possible key rotation mismatch")
        raise InternalServerError(
            message="A stored credential could not be decrypted. "
                    "Contact support if this issue persists (possible key rotation)."
        )
