"""
Transparent at-rest encryption for sensitive setting values.

Setting rows whose `key` is in PROTECTED_SETTING_KEYS get their `value`
column encrypted with the AUTH_PROVIDER_CRYPTO_KEY Fernet key (same key
that protects TOTP secrets). Encrypted values are prefixed with
`ENC_PREFIX` so reads can detect ciphertext vs. legacy plaintext and
auto-migrate on the next write.
"""
from __future__ import annotations

import logging
from typing import Optional

from sqlalchemy.orm import Session

from app.auth.totp import _fernet  # reuses AUTH_PROVIDER_CRYPTO_KEY
from app.models.setting import Setting

logger = logging.getLogger(__name__)

ENC_PREFIX = "enc:v1:"

# Settings whose value field contains secrets (API tokens, passwords).
# Values written under these keys are encrypted at rest; reads transparently
# decrypt. Pre-existing plaintext rows still readable until next write.
PROTECTED_SETTING_KEYS: frozenset[str] = frozenset({
    "wazuh_config",
    "smtp_config",
    "nessus_config",
})


def is_protected(key: str) -> bool:
    return key in PROTECTED_SETTING_KEYS


def encrypt_value(plaintext: str) -> str:
    """Return ENC_PREFIX + base64-ciphertext."""
    token = _fernet().encrypt(plaintext.encode()).decode()
    return f"{ENC_PREFIX}{token}"


def decrypt_value(stored: str) -> str:
    """Decrypt an ENC_PREFIX-tagged value. Passes plaintext through unchanged."""
    if not stored or not stored.startswith(ENC_PREFIX):
        return stored
    ciphertext = stored[len(ENC_PREFIX):]
    return _fernet().decrypt(ciphertext.encode()).decode()


def read_setting_value(db: Session, key: str) -> Optional[str]:
    """Return decrypted setting value (or None if not set)."""
    row = db.query(Setting).filter(Setting.key == key).first()
    if not row or row.value is None:
        return None
    if is_protected(key):
        try:
            return decrypt_value(row.value)
        except Exception as e:
            logger.error("Failed to decrypt setting %s: %s", key, e)
            return None
    return row.value


def write_setting_value(
    db: Session,
    key: str,
    value: str,
    description: str = "",
) -> Setting:
    """Upsert a setting, encrypting if the key is protected."""
    stored = encrypt_value(value) if is_protected(key) else value
    row = db.query(Setting).filter(Setting.key == key).first()
    if row:
        row.value = stored
    else:
        row = Setting(key=key, value=stored, description=description)
        db.add(row)
    db.commit()
    db.refresh(row)
    return row
