"""Tests for FastAPI middleware and dependencies."""

from unittest.mock import patch

import pytest
from fastapi import Depends, FastAPI
from fastapi.testclient import TestClient

from conduit_auth import AuthSettings, AuthenticatedUser
from conduit_auth.fastapi import AuthMiddleware, get_current_user


def _create_app(settings: AuthSettings) -> FastAPI:
    """Create a test FastAPI app with auth middleware."""
    app = FastAPI()
    app.add_middleware(AuthMiddleware, settings=settings)

    @app.get("/api/protected")
    async def protected(user: AuthenticatedUser = Depends(get_current_user)):
        return {
            "tenant_id": user.tenant_id,
            "user_id": user.user_id,
            "email": user.email,
            "display_name": user.display_name,
        }

    @app.get("/health")
    async def health():
        return {"status": "ok"}

    @app.get("/not-api")
    async def not_api():
        return {"message": "public"}

    return app


class TestAuthMiddleware:
    def test_missing_auth_header(self, auth_settings):
        client = TestClient(_create_app(auth_settings))
        resp = client.get("/api/protected")

        assert resp.status_code == 401
        assert "Authorization" in resp.json()["error"]

    def test_invalid_auth_header(self, auth_settings):
        client = TestClient(_create_app(auth_settings))
        resp = client.get(
            "/api/protected", headers={"Authorization": "Basic abc"}
        )

        assert resp.status_code == 401

    def test_valid_token(self, auth_settings, make_token, jwks_keys):
        client = TestClient(_create_app(auth_settings))
        token = make_token()

        with patch(
            "conduit_auth.fastapi.middleware.get_key_cache"
        ) as mock_cache:
            mock_cache.return_value.get_keys_async = (
                lambda *a, **kw: _async_return(jwks_keys)
            )
            resp = client.get(
                "/api/protected",
                headers={"Authorization": f"Bearer {token}"},
            )

        assert resp.status_code == 200
        data = resp.json()
        assert data["email"] == "user@example.com"
        assert data["tenant_id"] == "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"

    def test_expired_token(self, auth_settings, make_token, jwks_keys):
        client = TestClient(_create_app(auth_settings))
        token = make_token(expired=True)

        with patch(
            "conduit_auth.fastapi.middleware.get_key_cache"
        ) as mock_cache:
            mock_cache.return_value.get_keys_async = (
                lambda *a, **kw: _async_return(jwks_keys)
            )
            resp = client.get(
                "/api/protected",
                headers={"Authorization": f"Bearer {token}"},
            )

        assert resp.status_code == 401
        assert "expired" in resp.json()["error"]

    def test_health_excluded(self, auth_settings):
        client = TestClient(_create_app(auth_settings))
        resp = client.get("/health")

        assert resp.status_code == 200

    def test_non_api_path_excluded(self, auth_settings):
        client = TestClient(_create_app(auth_settings))
        resp = client.get("/not-api")

        assert resp.status_code == 200

    def test_auth_disabled(self, test_client_id):
        settings = AuthSettings(
            ENTRA_CLIENT_ID=test_client_id, AUTH_DISABLED=True
        )
        client = TestClient(_create_app(settings))
        resp = client.get("/api/protected")

        assert resp.status_code == 200
        assert resp.json()["email"] == "dev@localhost"
        assert resp.json()["tenant_id"] == "dev-tenant-00000000"


async def _async_return(value):
    return value
