"""Tests for users router (/api/users)."""
import pytest
from httpx import AsyncClient
from sqlalchemy.ext.asyncio import AsyncSession

from src.backend.models.orm.user import User
from src.backend.tests.conftest import _create_user, auth_headers


class TestListUsers:
    """GET /api/users tests."""

    async def test_list_users_admin(
        self, client: AsyncClient, admin_user: User
    ):
        """Admin can list all users."""
        resp = await client.get("/api/users", headers=auth_headers(admin_user))
        assert resp.status_code == 200
        data = resp.json()
        assert isinstance(data, list)
        # At least the admin user
        assert len(data) >= 1
        usernames = [u["username"] for u in data]
        assert "admin" in usernames

    async def test_list_users_forbidden(
        self, client: AsyncClient, maker_user: User
    ):
        """Non-admin user cannot list users."""
        resp = await client.get("/api/users", headers=auth_headers(maker_user))
        assert resp.status_code == 403


class TestGetUser:
    """GET /api/users/{user_id} is not directly exposed; use get_me or list."""

    async def test_get_me(self, client: AsyncClient, maker_user: User):
        """GET /api/auth/me returns current user profile."""
        resp = await client.get(
            "/api/auth/me", headers=auth_headers(maker_user)
        )
        assert resp.status_code == 200
        data = resp.json()
        assert data["username"] == "maker"
        assert data["display_name"] == "Maker User"
        assert "Maker" in data["roles"]


class TestCreateUser:
    """POST /api/users tests."""

    async def test_create_user(
        self, client: AsyncClient, admin_user: User
    ):
        """Admin can create a new user."""
        resp = await client.post(
            "/api/users",
            headers=auth_headers(admin_user),
            json={
                "username": "newuser",
                "password": "NewPass123",
                "display_name": "New User",
                "roles": ["MeasurementTec"],
                "is_admin": False,
            },
        )
        assert resp.status_code == 201
        data = resp.json()
        assert data["username"] == "newuser"
        assert data["display_name"] == "New User"
        assert "MeasurementTec" in data["roles"]
        assert data["active"] is True

    async def test_create_user_duplicate_username(
        self, client: AsyncClient, admin_user: User
    ):
        """Creating a user with existing username returns 409."""
        resp = await client.post(
            "/api/users",
            headers=auth_headers(admin_user),
            json={
                "username": "admin",
                "password": "DupPass123",
                "display_name": "Dup",
                "roles": [],
            },
        )
        assert resp.status_code == 409


class TestUpdateUser:
    """PUT /api/users/{user_id} tests."""

    async def test_update_user(
        self,
        client: AsyncClient,
        admin_user: User,
        maker_user: User,
    ):
        """Admin can update another user."""
        resp = await client.put(
            f"/api/users/{maker_user.id}",
            headers=auth_headers(admin_user),
            json={"display_name": "Updated Maker"},
        )
        assert resp.status_code == 200
        assert resp.json()["display_name"] == "Updated Maker"


class TestDeleteUser:
    """DELETE /api/users/{user_id} tests."""

    async def test_delete_user(
        self,
        client: AsyncClient,
        admin_user: User,
        db_session: AsyncSession,
    ):
        """Admin can soft-delete another user."""
        target = await _create_user(
            db_session, username="to_delete", display_name="Delete Me"
        )
        resp = await client.delete(
            f"/api/users/{target.id}", headers=auth_headers(admin_user)
        )
        assert resp.status_code == 204

    async def test_delete_self_forbidden(
        self, client: AsyncClient, admin_user: User
    ):
        """Admin cannot deactivate themselves."""
        resp = await client.delete(
            f"/api/users/{admin_user.id}", headers=auth_headers(admin_user)
        )
        assert resp.status_code == 400
        assert "yourself" in resp.json()["detail"]


class TestProfile:
    """PUT /api/auth/me tests."""

    async def test_update_me(self, client: AsyncClient, maker_user: User):
        """User can update own profile."""
        resp = await client.put(
            "/api/auth/me",
            headers=auth_headers(maker_user),
            json={"display_name": "My New Name", "theme_pref": "dark"},
        )
        assert resp.status_code == 200
        data = resp.json()
        assert data["display_name"] == "My New Name"
        assert data["theme_pref"] == "dark"

    async def test_change_password_not_in_profile(
        self, client: AsyncClient, maker_user: User
    ):
        """Profile update schema does not accept password field (422)."""
        resp = await client.put(
            "/api/auth/me",
            headers=auth_headers(maker_user),
            json={"password": "HackedPass1"},
        )
        # The field is simply ignored (exclude_unset), so no change should occur
        # but the request itself is valid (empty update)
        assert resp.status_code == 200
