"""Chiffrement des secrets d'intégration (`app.core.crypto`).

Ce que ces tests protègent : une clé client qui part en base **doit** être illisible sans
`CREDENTIALS_KEY`, et une clé illisible **doit** produire une erreur qu'on peut montrer à
l'utilisateur, jamais un 500 opaque au fond d'un job.
"""

import pytest
from cryptography.fernet import Fernet

from app.core import crypto
from app.core.config import get_settings
from app.core.errors import AppError

SECRET = "mangools-live-key-abcd1234"


def test_round_trip() -> None:
    token = crypto.encrypt(SECRET)
    assert crypto.decrypt(token) == SECRET


def test_ciphertext_does_not_contain_the_secret() -> None:
    """Le test le plus bête et le plus utile : rien du secret ne doit rester lisible."""
    token = crypto.encrypt(SECRET)
    assert SECRET not in token
    assert "abcd1234" not in token


def test_two_encryptions_of_the_same_secret_differ() -> None:
    """Fernet sale et horodate : deux jetons du même secret ne se ressemblent pas.

    C'est ce qui interdit de déduire « ces deux organisations ont la même clé » d'un dump.
    """
    assert crypto.encrypt(SECRET) != crypto.encrypt(SECRET)


def test_hint_shows_only_the_tail() -> None:
    assert crypto.hint(SECRET) == "••••••1234"
    assert SECRET[:-4] not in crypto.hint(SECRET)


def test_decrypt_with_another_key_is_a_conflict(monkeypatch: pytest.MonkeyPatch) -> None:
    """Clé de chiffrement changée (ou base restaurée sans elle) : 409 explicite, pas un 500."""
    token = crypto.encrypt(SECRET)
    monkeypatch.setenv("CREDENTIALS_KEY", Fernet.generate_key().decode())
    get_settings.cache_clear()
    try:
        with pytest.raises(AppError) as caught:
            crypto.decrypt(token)
        assert caught.value.status == 409
        assert "ressaisissez" in caught.value.detail.lower()
    finally:
        get_settings.cache_clear()


def test_missing_key_says_what_to_do(monkeypatch: pytest.MonkeyPatch) -> None:
    #  Retirer la variable d'environnement ne suffit **pas** : `Settings` déclare
    #  `env_file=".env"`, donc pydantic-settings relit le fichier et retrouve la clé. Le test
    #  passait tant que personne n'avait de `CREDENTIALS_KEY` dans son `api/.env` — c'est-à-dire
    #  tant que personne n'avait branché une intégration en local. Il devenait rouge sur le
    #  poste du premier développeur qui s'en servait vraiment, ce qui est exactement le moment
    #  où une suite doit rester verte.
    #
    #  On neutralise donc la **source** que lit `_fernet`, pas l'environnement.
    settings = get_settings().model_copy(update={"credentials_key": None})
    monkeypatch.setattr(crypto, "get_settings", lambda: settings)
    with pytest.raises(crypto.CredentialsKeyMissing) as caught:
        crypto.encrypt(SECRET)
    assert "CREDENTIALS_KEY" in str(caught.value)
    assert "Fernet.generate_key" in str(caught.value)


def test_invalid_key_is_refused(monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setenv("CREDENTIALS_KEY", "pas-une-cle-fernet")
    get_settings.cache_clear()
    try:
        with pytest.raises(crypto.CredentialsKeyMissing):
            crypto.encrypt(SECRET)
    finally:
        get_settings.cache_clear()
