"""
Fernet encryption field for LLM API keys.

Uses LLM_API_SECRETS_ENCRYPTION_KEY from settings if available,
otherwise derives a key from Django's SECRET_KEY (Themis pattern).
"""

import base64
import hashlib

from cryptography.fernet import Fernet, InvalidToken
from django.conf import settings
from django.db import models


def _get_fernet():
    """
    Get a Fernet cipher using the configured encryption key.

    Checks for LLM_API_SECRETS_ENCRYPTION_KEY first, then falls
    back to deriving a key from SECRET_KEY (Themis pattern).
    """
    key = getattr(settings, "LLM_API_SECRETS_ENCRYPTION_KEY", None)
    if key:
        return Fernet(key.encode() if isinstance(key, str) else key)
    # Fallback: derive from SECRET_KEY like Themis
    secret = settings.SECRET_KEY.encode("utf-8")
    digest = hashlib.sha256(secret).digest()
    derived_key = base64.urlsafe_b64encode(digest)
    return Fernet(derived_key)


class EncryptedCharField(models.CharField):
    """
    CharField that transparently encrypts/decrypts values using Fernet.

    Values are encrypted before saving to the database and decrypted
    when read. Supports blank/null values gracefully.
    """

    description = "Encrypted CharField for storing API secrets"

    def get_prep_value(self, value):
        """Encrypt before saving to DB."""
        if value is None or value == "":
            return value
        cipher = _get_fernet()
        encrypted = cipher.encrypt(value.encode("utf-8"))
        return base64.b64encode(encrypted).decode("utf-8")

    def from_db_value(self, value, expression, connection):
        """Decrypt when loading from DB."""
        if value is None or value == "":
            return value
        try:
            cipher = _get_fernet()
            encrypted = base64.b64decode(value)
            return cipher.decrypt(encrypted).decode("utf-8")
        except (InvalidToken, Exception):
            return value

    def to_python(self, value):
        if isinstance(value, str) or value is None:
            return value
        return str(value)
