"""Tests for user management endpoints (/api/users/*)."""
import pytest


# ---------------------------------------------------------------------------
# Shared fixtures
# ---------------------------------------------------------------------------

@pytest.fixture
def registered_user(client):
    """Register a test user and return the API response data."""
    c, _ = client
    response = c.post("/api/users/register", json={
        "email": "test@example.com",
        "displayName": "Test User",
        "photoURL": "https://example.com/photo.jpg",
    })
    assert response.status_code == 200
    return response.json()


# ---------------------------------------------------------------------------
# POST /api/users/register
# ---------------------------------------------------------------------------

class TestRegisterUser:
    def test_register_new_user_returns_200(self, client):
        c, _ = client
        response = c.post("/api/users/register", json={"email": "new@example.com", "displayName": "New User"})
        assert response.status_code == 200

    def test_register_returns_user_fields(self, client):
        c, _ = client
        response = c.post("/api/users/register", json={"email": "new@example.com", "displayName": "New User"})
        data = response.json()
        assert data["email"] == "new@example.com"
        assert data["displayName"] == "New User"
        assert "id" in data
        assert "createdAt" in data
        assert "updatedAt" in data

    def test_register_returns_registered_message(self, client):
        c, _ = client
        response = c.post("/api/users/register", json={"email": "brand_new@example.com"})
        assert response.json()["message"] == "User registered successfully"

    def test_register_existing_user_is_idempotent(self, client):
        c, _ = client
        payload = {"email": "existing@example.com"}
        c.post("/api/users/register", json=payload)
        response = c.post("/api/users/register", json=payload)
        assert response.status_code == 200
        assert response.json()["message"] == "User already exists"

    def test_register_idempotent_returns_same_id(self, client):
        c, _ = client
        payload = {"email": "same@example.com"}
        r1 = c.post("/api/users/register", json=payload).json()
        r2 = c.post("/api/users/register", json=payload).json()
        assert r1["id"] == r2["id"]

    def test_register_uses_email_prefix_as_default_display_name(self, client):
        c, _ = client
        response = c.post("/api/users/register", json={"email": "johndoe@example.com"})
        assert response.json()["displayName"] == "johndoe"

    def test_register_default_theme_is_light(self, client):
        c, _ = client
        response = c.post("/api/users/register", json={"email": "x@example.com"})
        assert response.json()["theme"] == "light"

    def test_register_missing_email_returns_422(self, client):
        c, _ = client
        response = c.post("/api/users/register", json={"displayName": "No Email"})
        assert response.status_code == 422

    def test_register_without_optional_fields(self, client):
        c, _ = client
        response = c.post("/api/users/register", json={"email": "minimal@example.com"})
        assert response.status_code == 200
        assert response.json()["photoURL"] is None


# ---------------------------------------------------------------------------
# GET /api/users/by-email/{email}
# ---------------------------------------------------------------------------

class TestGetUserByEmail:
    def test_returns_existing_user(self, client, registered_user):
        c, _ = client
        email = registered_user["email"]
        response = c.get(f"/api/users/by-email/{email}")
        assert response.status_code == 200
        assert response.json()["email"] == email

    def test_returns_all_user_fields(self, client, registered_user):
        c, _ = client
        response = c.get(f"/api/users/by-email/{registered_user['email']}")
        data = response.json()
        for field in ("id", "email", "displayName", "theme", "createdAt", "updatedAt"):
            assert field in data

    def test_nonexistent_email_returns_404(self, client):
        c, _ = client
        response = c.get("/api/users/by-email/ghost@example.com")
        assert response.status_code == 404
        assert "User not found" in response.json()["detail"]


# ---------------------------------------------------------------------------
# GET /api/users/{user_id}
# ---------------------------------------------------------------------------

class TestGetUserById:
    def test_returns_existing_user(self, client, registered_user):
        c, _ = client
        user_id = registered_user["id"]
        response = c.get(f"/api/users/{user_id}")
        assert response.status_code == 200
        assert response.json()["id"] == user_id

    def test_invalid_object_id_format_returns_400(self, client):
        c, _ = client
        response = c.get("/api/users/not-a-valid-objectid")
        assert response.status_code == 400

    def test_nonexistent_valid_id_returns_404(self, client):
        c, _ = client
        response = c.get("/api/users/507f1f77bcf86cd799439011")
        assert response.status_code == 404


# ---------------------------------------------------------------------------
# PUT /api/users/{user_id}
# ---------------------------------------------------------------------------

class TestUpdateUser:
    def test_update_display_name(self, client, registered_user):
        c, _ = client
        user_id = registered_user["id"]
        response = c.put(f"/api/users/{user_id}", json={"displayName": "Updated Name"})
        assert response.status_code == 200
        assert response.json()["displayName"] == "Updated Name"

    def test_update_theme_to_dark(self, client, registered_user):
        c, _ = client
        user_id = registered_user["id"]
        response = c.put(f"/api/users/{user_id}", json={"theme": "dark"})
        assert response.status_code == 200
        assert response.json()["theme"] == "dark"

    def test_update_photo_url(self, client, registered_user):
        c, _ = client
        user_id = registered_user["id"]
        new_url = "https://new-photo.example.com/pic.jpg"
        response = c.put(f"/api/users/{user_id}", json={"photoURL": new_url})
        assert response.status_code == 200
        assert response.json()["photoURL"] == new_url

    def test_update_persists_to_database(self, client, registered_user):
        c, _ = client
        user_id = registered_user["id"]
        c.put(f"/api/users/{user_id}", json={"displayName": "Persisted Name"})
        response = c.get(f"/api/users/{user_id}")
        assert response.json()["displayName"] == "Persisted Name"

    def test_partial_update_does_not_clear_other_fields(self, client, registered_user):
        c, _ = client
        user_id = registered_user["id"]
        # Update only theme
        c.put(f"/api/users/{user_id}", json={"theme": "dark"})
        response = c.get(f"/api/users/{user_id}")
        data = response.json()
        assert data["theme"] == "dark"
        assert data["displayName"] == "Test User"  # original value preserved

    def test_update_nonexistent_user_returns_404(self, client):
        c, _ = client
        response = c.put("/api/users/507f1f77bcf86cd799439011", json={"displayName": "X"})
        assert response.status_code == 404

    def test_update_invalid_id_format_returns_400(self, client):
        c, _ = client
        response = c.put("/api/users/bad-id", json={"displayName": "X"})
        assert response.status_code == 400


# ---------------------------------------------------------------------------
# DELETE /api/users/{user_id}
# ---------------------------------------------------------------------------

class TestDeleteUser:
    def test_delete_user_returns_200(self, client, registered_user):
        c, _ = client
        response = c.delete(f"/api/users/{registered_user['id']}")
        assert response.status_code == 200

    def test_delete_user_returns_deletion_counts(self, client, registered_user):
        c, _ = client
        response = c.delete(f"/api/users/{registered_user['id']}")
        data = response.json()
        assert data["user_deleted"] == 1
        assert "entries_deleted" in data

    def test_delete_user_makes_them_unretrievable(self, client, registered_user):
        c, _ = client
        user_id = registered_user["id"]
        c.delete(f"/api/users/{user_id}")
        response = c.get(f"/api/users/{user_id}")
        assert response.status_code == 404

    def test_delete_user_also_deletes_their_entries(self, client, registered_user):
        c, _ = client
        user_id = registered_user["id"]
        # Create 2 entries for this user
        for _ in range(2):
            c.post(f"/api/entries/{user_id}", json={
                "encryption": {
                    "encrypted": True,
                    "ciphertext": "dGVzdA==",
                    "nonce": "bm9uY2U=",
                    "algorithm": "XSalsa20-Poly1305",
                }
            })
        response = c.delete(f"/api/users/{user_id}")
        assert response.json()["entries_deleted"] == 2

    def test_delete_nonexistent_user_returns_404(self, client):
        c, _ = client
        response = c.delete("/api/users/507f1f77bcf86cd799439011")
        assert response.status_code == 404

    def test_delete_invalid_id_format_returns_400(self, client):
        c, _ = client
        response = c.delete("/api/users/bad-id")
        assert response.status_code == 400
