"""Unit tests for RequireMetaInfo and GetMetaInfo dependencies."""

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

from hezor_common.security.jwt import encode_jwt_with_pem
from hezor_common.transfer.base_sdk.constants import (
    ANONYMOUS_HEADER_PRIVATE_KEY,
    ANONYMOUS_HEADER_PRIVATE_KEY_PASSWORD,
)
from hezor_common.transfer.base_sdk.meta_info import MetaInfo
from hezor_common.transfer.middlewares import (
    CredentialMiddleware,
    GetMetaInfo,
    RequireMetaInfo,
    get_meta_info,
    require_meta_info,
)


@pytest.fixture
def test_app():
    """Create a test FastAPI application."""
    app = FastAPI()
    return app


@pytest.fixture
def valid_meta_info():
    """Create a valid MetaInfo object."""
    return MetaInfo(
        subject="测试主体",
        subject_code="test_001",
        caller_id="user_123",
        data_coverage="202401-202412",
        creation_slug="test_model",
        creation_name="测试模型",
    )


@pytest.fixture
def valid_meta_jwt(valid_meta_info):
    """Create a valid MetaInfo JWT token."""
    return encode_jwt_with_pem(
        ANONYMOUS_HEADER_PRIVATE_KEY.encode("utf-8"),
        valid_meta_info.model_dump(),
        password=ANONYMOUS_HEADER_PRIVATE_KEY_PASSWORD,
    )


class TestRequireMetaInfoDefault:
    """Test default require_meta_info instance."""

    def test_require_meta_info_with_valid_jwt(self, test_app, valid_meta_jwt):
        """Test require_meta_info with valid MetaInfo JWT."""
        test_app.add_middleware(CredentialMiddleware)

        @test_app.get("/protected")
        async def protected_route(meta: MetaInfo = Depends(require_meta_info)):
            return {"subject": meta.subject, "subject_code": meta.subject_code}

        client = TestClient(test_app)
        response = client.get("/protected", headers={"X-META-INFO": valid_meta_jwt})
        assert response.status_code == 200
        assert response.json()["subject"] == "测试主体"
        assert response.json()["subject_code"] == "test_001"

    def test_require_meta_info_without_jwt(self, test_app):
        """Test require_meta_info without MetaInfo JWT."""
        test_app.add_middleware(CredentialMiddleware)

        @test_app.get("/protected")
        async def protected_route(meta: MetaInfo = Depends(require_meta_info)):
            return {"subject": meta.subject}

        client = TestClient(test_app)
        response = client.get("/protected")
        assert response.status_code == 401
        assert response.json()["detail"] == "MetaInfo required"
        assert "WWW-Authenticate" in response.headers
        assert "X-META-INFO" in response.headers["WWW-Authenticate"]

    def test_require_meta_info_with_invalid_jwt(self, test_app):
        """Test require_meta_info with invalid MetaInfo JWT."""
        test_app.add_middleware(CredentialMiddleware)

        @test_app.get("/protected")
        async def protected_route(meta: MetaInfo = Depends(require_meta_info)):
            return {"subject": meta.subject}

        client = TestClient(test_app)
        response = client.get("/protected", headers={"X-META-INFO": "invalid-jwt"})
        assert response.status_code == 401
        assert "Invalid MetaInfo JWT" in response.json()["detail"]
        assert "WWW-Authenticate" in response.headers


class TestRequireMetaInfoCustomRealm:
    """Test RequireMetaInfo with custom realm."""

    def test_custom_realm_in_header(self, test_app):
        """Test custom realm appears in WWW-Authenticate header."""
        test_app.add_middleware(CredentialMiddleware)

        @test_app.get("/admin")
        async def admin_route(meta: MetaInfo = Depends(RequireMetaInfo("Admin area"))):
            return {"subject": meta.subject}

        client = TestClient(test_app)
        response = client.get("/admin")
        assert response.status_code == 401
        assert "WWW-Authenticate" in response.headers
        assert 'realm="Admin area"' in response.headers["WWW-Authenticate"]

    def test_custom_realm_with_valid_jwt(self, test_app, valid_meta_jwt):
        """Test custom realm with valid MetaInfo JWT."""
        test_app.add_middleware(CredentialMiddleware)

        @test_app.get("/admin")
        async def admin_route(meta: MetaInfo = Depends(RequireMetaInfo("Admin area"))):
            return {"subject": meta.subject}

        client = TestClient(test_app)
        response = client.get("/admin", headers={"X-META-INFO": valid_meta_jwt})
        assert response.status_code == 200
        assert response.json()["subject"] == "测试主体"

    def test_default_realm_value(self, test_app):
        """Test default realm value."""
        test_app.add_middleware(CredentialMiddleware)

        @test_app.get("/protected")
        async def protected_route(meta: MetaInfo = Depends(require_meta_info)):
            return {"subject": meta.subject}

        client = TestClient(test_app)
        response = client.get("/protected")
        assert response.status_code == 401
        assert 'realm="Access to the protected resource"' in response.headers["WWW-Authenticate"]


class TestGetMetaInfoDefault:
    """Test default get_meta_info instance."""

    def test_get_meta_info_with_valid_jwt(self, test_app, valid_meta_jwt):
        """Test get_meta_info with valid MetaInfo JWT."""
        test_app.add_middleware(CredentialMiddleware)

        @test_app.get("/optional")
        async def optional_route(meta: MetaInfo | None = Depends(get_meta_info)):
            if meta:
                return {"subject": meta.subject, "has_meta": True}
            return {"has_meta": False}

        client = TestClient(test_app)
        response = client.get("/optional", headers={"X-META-INFO": valid_meta_jwt})
        assert response.status_code == 200
        assert response.json()["has_meta"] is True
        assert response.json()["subject"] == "测试主体"

    def test_get_meta_info_without_jwt(self, test_app):
        """Test get_meta_info without MetaInfo JWT."""
        test_app.add_middleware(CredentialMiddleware)

        @test_app.get("/optional")
        async def optional_route(meta: MetaInfo | None = Depends(get_meta_info)):
            if meta:
                return {"subject": meta.subject, "has_meta": True}
            return {"has_meta": False}

        client = TestClient(test_app)
        response = client.get("/optional")
        assert response.status_code == 200
        assert response.json()["has_meta"] is False

    def test_get_meta_info_with_invalid_jwt(self, test_app):
        """Test get_meta_info with invalid MetaInfo JWT returns None."""
        test_app.add_middleware(CredentialMiddleware)

        @test_app.get("/optional")
        async def optional_route(meta: MetaInfo | None = Depends(get_meta_info)):
            if meta:
                return {"subject": meta.subject, "has_meta": True}
            return {"has_meta": False}

        client = TestClient(test_app)
        response = client.get("/optional", headers={"X-META-INFO": "invalid-jwt"})
        assert response.status_code == 200
        assert response.json()["has_meta"] is False


class TestGetMetaInfoClass:
    """Test GetMetaInfo class."""

    def test_get_meta_info_class_with_jwt(self, test_app, valid_meta_jwt):
        """Test GetMetaInfo class with valid JWT."""
        test_app.add_middleware(CredentialMiddleware)

        @test_app.get("/optional")
        async def optional_route(meta: MetaInfo | None = Depends(GetMetaInfo())):
            if meta:
                return {"subject": meta.subject, "has_meta": True}
            return {"has_meta": False}

        client = TestClient(test_app)
        response = client.get("/optional", headers={"X-META-INFO": valid_meta_jwt})
        assert response.status_code == 200
        assert response.json()["has_meta"] is True

    def test_get_meta_info_class_without_jwt(self, test_app):
        """Test GetMetaInfo class without JWT."""
        test_app.add_middleware(CredentialMiddleware)

        @test_app.get("/optional")
        async def optional_route(meta: MetaInfo | None = Depends(GetMetaInfo())):
            if meta:
                return {"subject": meta.subject, "has_meta": True}
            return {"has_meta": False}

        client = TestClient(test_app)
        response = client.get("/optional")
        assert response.status_code == 200
        assert response.json()["has_meta"] is False


class TestRequireMetaInfoEdgeCases:
    """Test edge cases and error scenarios."""

    def test_callable_class_behavior(self):
        """Test RequireMetaInfo is callable."""
        instance = RequireMetaInfo("Test realm")
        assert callable(instance)
        assert instance.realm == "Test realm"

    def test_realm_initialization(self):
        """Test realm initialization."""
        instance = RequireMetaInfo()
        assert instance.realm == "Access to the protected resource"

        custom_instance = RequireMetaInfo("Custom realm")
        assert custom_instance.realm == "Custom realm"

    def test_get_meta_info_callable(self):
        """Test GetMetaInfo is callable."""
        instance = GetMetaInfo()
        assert callable(instance)

    def test_multiple_routes_different_realms(self, test_app, valid_meta_jwt):
        """Test multiple routes with different realms."""
        test_app.add_middleware(CredentialMiddleware)

        @test_app.get("/api")
        async def api_route(meta: MetaInfo = Depends(RequireMetaInfo("API access"))):
            _ = meta  # Dependency injection test
            return {"route": "api"}

        @test_app.get("/admin")
        async def admin_route(meta: MetaInfo = Depends(RequireMetaInfo("Admin area"))):
            _ = meta  # Dependency injection test
            return {"route": "admin"}

        client = TestClient(test_app)

        # Test API route without JWT
        response = client.get("/api")
        assert response.status_code == 401
        assert 'realm="API access"' in response.headers["WWW-Authenticate"]

        # Test admin route without JWT
        response = client.get("/admin")
        assert response.status_code == 401
        assert 'realm="Admin area"' in response.headers["WWW-Authenticate"]

        # Test both with valid JWT
        response = client.get("/api", headers={"X-META-INFO": valid_meta_jwt})
        assert response.status_code == 200

        response = client.get("/admin", headers={"X-META-INFO": valid_meta_jwt})
        assert response.status_code == 200

    def test_no_middleware_configured(self, test_app):
        """Test behavior when CredentialMiddleware is not configured."""

        @test_app.get("/protected")
        async def protected_route(meta: MetaInfo = Depends(require_meta_info)):
            return {"subject": meta.subject}

        client = TestClient(test_app)
        response = client.get("/protected")
        assert response.status_code == 401
        assert response.json()["detail"] == "MetaInfo required"

    def test_malformed_meta_info_in_state(self, test_app):
        """Test handling of MetaInfo validation error from middleware."""
        test_app.add_middleware(CredentialMiddleware)

        @test_app.get("/protected")
        async def protected_route(meta: MetaInfo = Depends(require_meta_info)):
            return {"subject": meta.subject}

        client = TestClient(test_app)
        # Use invalid JWT to trigger validation error
        response = client.get("/protected", headers={"X-META-INFO": "malformed"})
        assert response.status_code == 401
        assert "Invalid MetaInfo JWT" in response.json()["detail"]
