"""Symmetric encryption for credentials we have to store.

Used today for the per-user Nextcloud app password — we need the
plaintext to put it in an outgoing HTTP Basic header, so a one-way
hash won't do. Key is derived from `settings.secret_key` via SHA-256
so existing deployments don't need a separate KMS dance, and a stable
SECRET_KEY rotates these credentials automatically.

Fernet is symmetric AES-128-CBC + HMAC-SHA256 with a versioned
ciphertext envelope; good enough for column-level secrecy in a
single-host homelab. Rotate by setting a new SECRET_KEY and asking
users to re-enter their app password.
"""
import base64
import hashlib
from typing import Optional

from cryptography.fernet import Fernet, InvalidToken

from app.config import settings


def _fernet() -> Fernet:
    # Fernet requires a 32-byte url-safe base64 key. SHA-256 of the
    # configured secret gives us exactly 32 bytes; b64-urlsafe-encode
    # to fit the API contract.
    digest = hashlib.sha256(settings.secret_key.encode("utf-8")).digest()
    return Fernet(base64.urlsafe_b64encode(digest))


def encrypt(plaintext: str) -> str:
    """Return a base64 token that can be stored in a VARCHAR column."""
    return _fernet().encrypt(plaintext.encode("utf-8")).decode("ascii")


def decrypt(token: Optional[str]) -> Optional[str]:
    """Inverse of encrypt. Returns None for None / empty input. Raises
    on tampered or wrong-key tokens — callers should treat that as
    "credential unset" rather than crashing the request."""
    if not token:
        return None
    try:
        return _fernet().decrypt(token.encode("ascii")).decode("utf-8")
    except InvalidToken:
        return None
