"""Exception handler tests."""
# ruff: noqa: I001 - Imports structured for Jinja2 template conditionals

import pytest
from httpx import AsyncClient

from app.core.config import settings


@pytest.mark.anyio
async def test_not_found_error_format(client: AsyncClient):
    """Test that 404 errors return proper JSON format."""
    response = await client.get(f"{settings.API_V1_STR}/nonexistent-endpoint")
    assert response.status_code == 404
    # FastAPI returns 404 for unknown routes


from unittest.mock import AsyncMock, MagicMock

ServiceMock = AsyncMock

from httpx import ASGITransport
from app.api.deps import get_redis
from app.api.deps import get_db_session
from app.main import app


@pytest.mark.anyio
async def test_authentication_error_returns_401(client: AsyncClient):
    """Test that authentication errors return 401 with proper headers."""
    response = await client.get(
        f"{settings.API_V1_STR}/users/me",
        headers={"Authorization": "Bearer invalid-token"},
    )
    assert response.status_code == 401
    assert "WWW-Authenticate" in response.headers


@pytest.mark.anyio
async def test_missing_auth_returns_401(client: AsyncClient):
    """Test that missing authentication returns 401."""
    response = await client.get(f"{settings.API_V1_STR}/users/me")
    assert response.status_code == 401


@pytest.fixture
def mock_user_service_with_errors() -> MagicMock:
    """Create mock user service that raises errors."""
    from app.core.exceptions import AlreadyExistsError, AuthenticationError

    service = MagicMock()
    service.register = ServiceMock(
        side_effect=AlreadyExistsError(message="Email already registered")
    )
    service.authenticate = ServiceMock(
        side_effect=AuthenticationError(message="Invalid credentials")
    )
    return service


@pytest.fixture
async def error_client(
    mock_user_service_with_errors: MagicMock,
    mock_redis: MagicMock,
    mock_db_session,
) -> AsyncClient:
    """Client with mocked services that raise errors."""
    from app.api.deps import get_user_service

    app.dependency_overrides[get_user_service] = lambda: mock_user_service_with_errors
    app.dependency_overrides[get_redis] = lambda: mock_redis
    app.dependency_overrides[get_db_session] = lambda: mock_db_session

    async with AsyncClient(
        transport=ASGITransport(app=app),
        base_url="http://test",
    ) as ac:
        yield ac

    app.dependency_overrides.clear()


@pytest.mark.anyio
async def test_register_duplicate_email_returns_409(error_client: AsyncClient):
    """Test that registering with existing email returns 409."""
    response = await error_client.post(
        f"{settings.API_V1_STR}/auth/register",
        json={
            "email": "existing@example.com",
            "password": "password123",
            "full_name": "Test User",
        },
    )
    assert response.status_code == 409
    data = response.json()
    assert "error" in data
    assert data["error"]["code"] == "ALREADY_EXISTS"


@pytest.mark.anyio
async def test_invalid_login_returns_401(error_client: AsyncClient):
    """Test that invalid login credentials return 401."""
    response = await error_client.post(
        f"{settings.API_V1_STR}/auth/login",
        data={
            "username": "nonexistent@example.com",
            "password": "wrongpassword",
        },
    )
    assert response.status_code == 401
    data = response.json()
    assert "error" in data
    assert data["error"]["code"] == "AUTHENTICATION_ERROR"


@pytest.mark.anyio
async def test_missing_api_key_returns_401(client: AsyncClient):
    """Test that missing API key returns 401."""
    # Health endpoint might not require auth, but we test the middleware
    # For endpoints that require API key, they should return 401
    await client.get(f"{settings.API_V1_STR}/health")


@pytest.mark.anyio
async def test_invalid_api_key_returns_403(client: AsyncClient):
    """Test that invalid API key returns 403."""
    # Health endpoint might not require auth
    await client.get(
        f"{settings.API_V1_STR}/health",
        headers={settings.API_KEY_HEADER: "invalid-key"},
    )
