"""Encrypted secrets vault. Credentials are stored as Fernet ciphertext in app_secret;
the key lives only in the environment (RECORDGOD_SECRET_KEY), never in the DB or the repo.
If the key isn't set we REFUSE to store secrets — never silently store them in plaintext."""
import os

from cryptography.fernet import Fernet, InvalidToken
from sqlalchemy import text

_key = os.getenv("RECORDGOD_SECRET_KEY")
_fernet = Fernet(_key.encode()) if _key else None


class SecretsUnconfigured(RuntimeError):
    pass


def _f():
    if _fernet is None:
        raise SecretsUnconfigured(
            "RECORDGOD_SECRET_KEY not set — refusing to store secrets unencrypted")
    return _fernet


async def set_secret(db, name, plaintext):
    enc = _f().encrypt(plaintext.encode())
    await db.execute(text("""
        INSERT INTO app_secret (name, value_enc, updated_at) VALUES (:n, :v, now())
        ON CONFLICT (name) DO UPDATE SET value_enc = EXCLUDED.value_enc, updated_at = now()
    """), {"n": name, "v": enc})
    await db.commit()


async def get_secret(db, name):
    """Decrypt one secret for server-side use (e.g. the Woo client). Never exposed over HTTP."""
    row = (await db.execute(text("SELECT value_enc FROM app_secret WHERE name = :n"),
                            {"n": name})).first()
    if not row:
        return None
    try:
        return _f().decrypt(bytes(row[0])).decode()
    except InvalidToken:
        return None  # key rotated / corrupt — treat as not-set rather than crash


async def list_secret_names(db):
    rows = await db.execute(text("SELECT name, updated_at FROM app_secret ORDER BY name"))
    return [{"name": r[0], "updated_at": r[1].isoformat() if r[1] else None}
            for r in rows]
