"""Tests for VikdataClient."""

import pytest
import respx
from httpx import Response

from vikdata import VikdataClient
from vikdata.exceptions import (
    AuthenticationError,
    ResourceNotFoundError,
    ValidationError,
)
from vikdata.models.auth import LoginRequest, TokenResponse, UserResponse


@pytest.fixture
def mock_base_url() -> str:
    """Mock base URL for tests."""
    return "https://api.test.vikdata.com"


@pytest.fixture
async def client(mock_base_url: str) -> VikdataClient:
    """Create a test client."""
    async with VikdataClient(base_url=mock_base_url, api_key="test_key") as c:
        yield c


@respx.mock
@pytest.mark.asyncio
async def test_login_success(mock_base_url: str) -> None:
    """Test successful login."""
    # Mock response
    mock_response = {
        "access_token": "test_access_token",
        "refresh_token": "test_refresh_token",
        "token_type": "bearer",
        "expires_in": 3600,
    }

    # Mock the API endpoint
    route = respx.post(f"{mock_base_url}/api/v1/auth/login").mock(
        return_value=Response(200, json=mock_response)
    )

    async with VikdataClient(base_url=mock_base_url) as client:
        response = await client.auth.login(
            email="test@example.com",
            password="password123",
        )

        assert isinstance(response, TokenResponse)
        assert response.access_token == "test_access_token"
        assert response.refresh_token == "test_refresh_token"
        assert response.token_type == "bearer"
        assert response.expires_in == 3600

        # Check that access token was set on client
        assert client.access_token == "test_access_token"

        # Verify request was made correctly
        assert route.called
        request = route.calls[0].request
        assert request.method == "POST"


@respx.mock
@pytest.mark.asyncio
async def test_login_invalid_credentials(mock_base_url: str) -> None:
    """Test login with invalid credentials."""
    # Mock error response
    respx.post(f"{mock_base_url}/api/v1/auth/login").mock(
        return_value=Response(
            401,
            json={"detail": "Invalid email or password"},
        )
    )

    async with VikdataClient(base_url=mock_base_url) as client:
        with pytest.raises(AuthenticationError, match="Invalid email or password"):
            await client.auth.login(
                email="test@example.com",
                password="wrong_password",
            )


@respx.mock
@pytest.mark.asyncio
async def test_me_success(mock_base_url: str) -> None:
    """Test getting current user info."""
    # Mock response
    mock_response = {
        "id": "123e4567-e89b-12d3-a456-426614174000",
        "tenant_id": "123e4567-e89b-12d3-a456-426614174001",
        "email": "test@example.com",
        "name": "Test User",
        "permissions": ["users:read", "roles:read"],
        "roles": ["user", "analyst"],
    }

    respx.get(f"{mock_base_url}/api/v1/auth/me").mock(
        return_value=Response(200, json=mock_response)
    )

    async with VikdataClient(
        base_url=mock_base_url,
        access_token="test_token",
    ) as client:
        response = await client.auth.me()

        assert isinstance(response, UserResponse)
        assert response.email == "test@example.com"
        assert response.name == "Test User"
        assert "users:read" in response.permissions
        assert "user" in response.roles


@respx.mock
@pytest.mark.asyncio
async def test_roles_list_success(mock_base_url: str) -> None:
    """Test listing roles."""
    # Mock response
    mock_response = {
        "data": [
            {
                "id": "123e4567-e89b-12d3-a456-426614174000",
                "name": "Admin",
                "slug": "admin",
                "description": "Administrator role",
                "tenant_id": None,
                "is_system": True,
                "permissions": [],
            }
        ],
        "total": 1,
    }

    respx.get(f"{mock_base_url}/api/v1/roles").mock(
        return_value=Response(200, json=mock_response)
    )

    async with VikdataClient(
        base_url=mock_base_url,
        access_token="test_token",
    ) as client:
        response = await client.roles.list()

        assert len(response.data) == 1
        assert response.data[0].name == "Admin"
        assert response.data[0].is_system is True
        assert response.total == 1


@respx.mock
@pytest.mark.asyncio
async def test_resource_not_found(mock_base_url: str) -> None:
    """Test resource not found error."""
    respx.get(f"{mock_base_url}/api/v1/roles/123e4567-e89b-12d3-a456-426614174000").mock(
        return_value=Response(404, json={"detail": "Role not found"})
    )

    async with VikdataClient(
        base_url=mock_base_url,
        access_token="test_token",
    ) as client:
        from uuid import UUID

        with pytest.raises(ResourceNotFoundError, match="Role not found"):
            await client.roles.get(UUID("123e4567-e89b-12d3-a456-426614174000"))


@respx.mock
@pytest.mark.asyncio
async def test_validation_error(mock_base_url: str) -> None:
    """Test validation error."""
    respx.post(f"{mock_base_url}/api/v1/roles").mock(
        return_value=Response(400, json={"detail": "Invalid slug format"})
    )

    async with VikdataClient(
        base_url=mock_base_url,
        access_token="test_token",
    ) as client:
        with pytest.raises(ValidationError, match="Invalid slug format"):
            await client.roles.create(
                name="Test Role",
                slug="invalid slug!",
            )


@pytest.mark.asyncio
async def test_client_context_manager(mock_base_url: str) -> None:
    """Test client async context manager."""
    async with VikdataClient(base_url=mock_base_url) as client:
        assert client._client is not None

    # Client should be closed after exiting context
    assert client._client is None
