"""
Tests pour POST /api/v1/auth/register
Couvre les AC 1-5 de la story 1.2 : Inscription Utilisateur
"""

import json
import pytest
from pathlib import Path
from fastapi.testclient import TestClient


REGISTER_URL = "/api/v1/auth/register"

VALID_PAYLOAD = {
    "email": "test@example.com",
    "password": "Password123!",
    "name": "Test User",
}


@pytest.fixture()
def users_file(tmp_path: Path) -> Path:
    """Fichier de stockage JSON isolé pour les tests."""
    return tmp_path / "users.json"


@pytest.fixture()
def client(users_file: Path, monkeypatch):
    """TestClient avec stockage JSON isolé et rate limiting désactivé."""
    import services.auth_service as auth_svc

    monkeypatch.setattr(auth_svc, "USERS_FILE", users_file)
    monkeypatch.setattr(auth_svc, "USE_DATABASE", False)
    monkeypatch.setattr(auth_svc, "DATABASE_AVAILABLE", False)

    # Désactiver le rate limiting pour les tests
    import main as main_module
    from middleware.rate_limiting import RateLimitManager

    async def _check_request_allow(self, request):
        return True, "ok", "test"

    async def _check_translation_allow(self, request, file_size_mb=0):
        return True, "ok"

    monkeypatch.setattr(RateLimitManager, "check_request", _check_request_allow)
    monkeypatch.setattr(RateLimitManager, "check_translation", _check_translation_allow)

    from main import app

    return TestClient(app, raise_server_exceptions=True)


# ---------------------------------------------------------------------------
# AC1 : Inscription réussie
# ---------------------------------------------------------------------------


class TestRegistrationSuccess:
    """AC1 : inscription valide → 201 + données utilisateur"""

    def test_returns_201_on_success(self, client):
        response = client.post(REGISTER_URL, json=VALID_PAYLOAD)
        assert response.status_code == 201

    def test_response_contains_data_and_meta(self, client):
        response = client.post(REGISTER_URL, json=VALID_PAYLOAD)
        body = response.json()
        assert "data" in body
        assert "meta" in body

    def test_response_data_contains_id(self, client):
        response = client.post(REGISTER_URL, json=VALID_PAYLOAD)
        assert "id" in response.json()["data"]
        assert response.json()["data"]["id"]  # non vide

    def test_response_data_contains_correct_email(self, client):
        response = client.post(REGISTER_URL, json=VALID_PAYLOAD)
        assert response.json()["data"]["email"] == VALID_PAYLOAD["email"]

    def test_new_user_has_free_tier(self, client):
        """AC1 : nouvel utilisateur créé avec tier='free'"""
        response = client.post(REGISTER_URL, json=VALID_PAYLOAD)
        assert response.json()["data"]["tier"] == "free"


# ---------------------------------------------------------------------------
# AC2 : Hachage du mot de passe
# ---------------------------------------------------------------------------


class TestPasswordHashing:
    """AC2 : le mot de passe est haché avec passlib[bcrypt]"""

    def test_password_not_stored_as_plaintext(self, client, users_file: Path):
        password = "MySecret123!"
        client.post(
            REGISTER_URL,
            json={
                "email": "hash@example.com",
                "password": password,
                "name": "Hash User",
            },
        )

        assert users_file.exists(), (
            "Le fichier utilisateurs doit exister après inscription"
        )
        users_data = json.loads(users_file.read_text())

        for user in users_data.values():
            stored = user.get("password_hash", "")
            assert stored != password, (
                "Le mot de passe ne doit pas être stocké en clair"
            )
            assert len(stored) > 0, "Un hash doit être présent"

    def test_password_hash_uses_bcrypt(self, client, users_file: Path):
        """Le hash doit commencer par '$2b$' (bcrypt) lorsque passlib est disponible."""
        import services.auth_service as auth_svc

        if not auth_svc.PASSLIB_AVAILABLE:
            pytest.skip("passlib non disponible dans cet environnement")

        client.post(
            REGISTER_URL,
            json={
                "email": "bcrypt@example.com",
                "password": "BCrypt123!",
                "name": "BCrypt User",
            },
        )

        users_data = json.loads(users_file.read_text())
        for user in users_data.values():
            if user.get("email") == "bcrypt@example.com":
                assert user["password_hash"].startswith("$2b$"), (
                    "Le hash doit être au format bcrypt ($2b$)"
                )


# ---------------------------------------------------------------------------
# AC3 : Email dupliqué
# ---------------------------------------------------------------------------


class TestDuplicateEmail:
    """AC3 : email déjà utilisé → 400 EMAIL_EXISTS"""

    def test_duplicate_email_returns_400(self, client):
        client.post(REGISTER_URL, json=VALID_PAYLOAD)
        response = client.post(REGISTER_URL, json=VALID_PAYLOAD)
        assert response.status_code == 400

    def test_duplicate_email_error_code(self, client):
        client.post(REGISTER_URL, json=VALID_PAYLOAD)
        response = client.post(REGISTER_URL, json=VALID_PAYLOAD)
        assert response.json()["error"] == "EMAIL_EXISTS"

    def test_duplicate_email_has_message(self, client):
        client.post(REGISTER_URL, json=VALID_PAYLOAD)
        response = client.post(REGISTER_URL, json=VALID_PAYLOAD)
        assert "message" in response.json()
        assert response.json()["message"]


# ---------------------------------------------------------------------------
# AC4 : Email invalide
# ---------------------------------------------------------------------------


class TestInvalidEmail:
    """AC4 : format d'email invalide → 400 INVALID_EMAIL"""

    @pytest.mark.parametrize(
        "bad_email",
        [
            "not-an-email",
            "missing@domain",
            "@nodomain.com",
            "spaces in@email.com",
            "",
        ],
    )
    def test_invalid_email_returns_400(self, client, bad_email):
        response = client.post(
            REGISTER_URL,
            json={"email": bad_email, "password": "Password123!", "name": "Bad Email"},
        )
        assert response.status_code == 400

    def test_invalid_email_error_code(self, client):
        response = client.post(
            REGISTER_URL,
            json={
                "email": "not-an-email",
                "password": "Password123!",
                "name": "Bad Email",
            },
        )
        assert response.json()["error"] == "INVALID_EMAIL"

    def test_invalid_email_has_message(self, client):
        response = client.post(
            REGISTER_URL,
            json={
                "email": "not-an-email",
                "password": "Password123!",
                "name": "Bad Email",
            },
        )
        assert "message" in response.json()
        assert response.json()["message"]

    def test_missing_password_returns_invalid_request(self, client):
        response = client.post(
            REGISTER_URL,
            json={"email": "missing-password@example.com", "name": "Bad Payload"},
        )
        assert response.status_code == 400
        assert response.json()["error"] == "INVALID_REQUEST"

    def test_invalid_json_body_returns_invalid_request(self, client):
        response = client.post(
            REGISTER_URL,
            data="{bad-json",
            headers={"Content-Type": "application/json"},
        )
        assert response.status_code == 400
        assert response.json()["error"] == "INVALID_REQUEST"


class TestPasswordStrengthValidation:
    """Tests pour la validation de force du mot de passe"""

    def test_password_too_short_returns_weak_password(self, client):
        """Mot de passe < 8 caractères rejeté"""
        response = client.post(
            REGISTER_URL,
            json={
                "email": "weak@example.com",
                "password": "1234567",
                "name": "Test User",
            },
        )
        assert response.status_code == 400
        assert response.json()["error"] == "WEAK_PASSWORD"
        # Check for either the French message or English fallback
        msg = response.json()["message"]
        assert "8" in msg and ("caractères" in msg or "characters" in msg.lower())

    def test_password_missing_uppercase_returns_weak_password(self, client):
        """Mot de passe sans majuscule rejeté"""
        response = client.post(
            REGISTER_URL,
            json={
                "email": "weak@example.com",
                "password": "password123!",
                "name": "Test User",
            },
        )
        assert response.status_code == 400
        assert response.json()["error"] == "WEAK_PASSWORD"
        assert "majuscule" in response.json()["message"]

    def test_password_missing_lowercase_returns_weak_password(self, client):
        """Mot de passe sans minuscule rejeté"""
        response = client.post(
            REGISTER_URL,
            json={
                "email": "weak@example.com",
                "password": "PASSWORD123!",
                "name": "Test User",
            },
        )
        assert response.status_code == 400
        assert response.json()["error"] == "WEAK_PASSWORD"
        assert "minuscule" in response.json()["message"]

    def test_password_missing_digit_returns_weak_password(self, client):
        """Mot de passe sans chiffre rejeté"""
        response = client.post(
            REGISTER_URL,
            json={
                "email": "weak@example.com",
                "password": "PasswordOnly!",
                "name": "Test User",
            },
        )
        assert response.status_code == 400
        assert response.json()["error"] == "WEAK_PASSWORD"
        assert "chiffre" in response.json()["message"]

    def test_strong_password_accepted(self, client):
        """Mot de passe fort accepté"""
        response = client.post(
            REGISTER_URL,
            json={
                "email": "strong@example.com",
                "password": "StrongPass123!",
                "name": "Test User",
            },
        )
        assert response.status_code == 201
        assert "id" in response.json()["data"]


# ---------------------------------------------------------------------------
# AC5 : Versionnage d'API
# ---------------------------------------------------------------------------


class TestApiVersioning:
    """AC5 : endpoint accessible à /api/v1/auth/register"""

    def test_endpoint_accessible_at_v1_path(self, client):
        response = client.post("/api/v1/auth/register", json=VALID_PAYLOAD)
        assert response.status_code == 201

    @pytest.mark.skip(
        reason="Story 3.5: Legacy /api/auth paths are no longer supported. All endpoints must use /api/v1 prefix."
    )
    def test_legacy_path_still_works(self, client):
        """Le chemin herite /api/auth/register doit desormais retourner 404."""
        response = client.post("/api/auth/register", json=VALID_PAYLOAD)
        assert response.status_code in (200, 201), (
            "L'endpoint herite doit rester operationnel"
        )
