"""Encryption at rest using Fernet (AES-128-CBC + HMAC)."""

import base64
import logging
import os
from pathlib import Path

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

logger = logging.getLogger(__name__)

_SALT = b"sulci-encryption-v1"  # Static salt — key material is already random


class EncryptionManager:
    """Manages encryption/decryption for data at rest."""

    def __init__(self, data_dir: Path) -> None:
        self._data_dir = data_dir
        self._keyfile_path = data_dir / ".keyfile"
        self._fernet: Fernet | None = None

    def _ensure_keyfile(self) -> bytes:
        """Load or generate the encryption keyfile."""
        if self._keyfile_path.exists():
            return self._keyfile_path.read_bytes()

        # Generate 32 random bytes, base64-encode for storage
        raw_key = os.urandom(32)
        encoded = base64.urlsafe_b64encode(raw_key)
        self._data_dir.mkdir(parents=True, exist_ok=True)
        self._keyfile_path.write_bytes(encoded)
        os.chmod(self._keyfile_path, 0o600)
        logger.info("Generated new encryption keyfile at %s", self._keyfile_path)
        return encoded

    def _get_fernet(self) -> Fernet:
        """Get or create the Fernet cipher."""
        if self._fernet is not None:
            return self._fernet

        key_material = self._ensure_keyfile()

        # Derive a Fernet key from the keyfile contents using PBKDF2
        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA256(),
            length=32,
            salt=_SALT,
            iterations=480_000,
        )
        derived = kdf.derive(key_material)
        fernet_key = base64.urlsafe_b64encode(derived)
        self._fernet = Fernet(fernet_key)
        return self._fernet

    def encrypt(self, plaintext: str) -> str:
        """Encrypt a string, returning a base64 token."""
        fernet = self._get_fernet()
        return fernet.encrypt(plaintext.encode("utf-8")).decode("ascii")

    def decrypt(self, ciphertext: str) -> str:
        """Decrypt a Fernet token back to a string."""
        fernet = self._get_fernet()
        return fernet.decrypt(ciphertext.encode("ascii")).decode("utf-8")

    def encrypt_bytes(self, data: bytes) -> bytes:
        """Encrypt raw bytes."""
        fernet = self._get_fernet()
        return fernet.encrypt(data)

    def decrypt_bytes(self, data: bytes) -> bytes:
        """Decrypt raw bytes."""
        fernet = self._get_fernet()
        return fernet.decrypt(data)
