"""Unit tests for RequireCredentials and GetCredentials 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,
    GetCredentials,
    RequireCredentials,
    get_credentials,
    require_credentials,
)


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


@pytest.fixture
def valid_api_key():
    """Return a valid API key for testing."""
    return "test-api-key-12345"


@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 TestRequireCredentialsDefault:
    """Test default require_credentials instance."""

    def test_require_credentials_with_both_valid(self, test_app, valid_api_key, valid_meta_jwt):
        """Test require_credentials with both valid API key and MetaInfo."""
        test_app.add_middleware(CredentialMiddleware, api_key=valid_api_key)

        @test_app.get("/protected")
        async def protected_route(
            cred: tuple[str | None, MetaInfo | None] = Depends(require_credentials),
        ):
            api_key, meta_info = cred
            return {
                "has_api_key": api_key is not None,
                "has_meta_info": meta_info is not None,
                "subject": meta_info.subject if meta_info else None,
            }

        client = TestClient(test_app)
        response = client.get(
            "/protected",
            headers={
                "Authorization": f"Bearer {valid_api_key}",
                "X-META-INFO": valid_meta_jwt,
            },
        )
        assert response.status_code == 200
        data = response.json()
        assert data["has_api_key"] is True
        assert data["has_meta_info"] is True
        assert data["subject"] == "测试主体"

    def test_require_credentials_without_any(self, test_app, valid_api_key):
        """Test require_credentials without any credentials."""
        test_app.add_middleware(CredentialMiddleware, api_key=valid_api_key)

        @test_app.get("/protected")
        async def protected_route(
            cred: tuple[str | None, MetaInfo | None] = Depends(require_credentials),
        ):
            _ = cred  # Dependency injection test
            return {"status": "ok"}

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

    def test_require_credentials_with_only_api_key(self, test_app, valid_api_key):
        """Test require_credentials with only API key (missing MetaInfo)."""
        test_app.add_middleware(CredentialMiddleware, api_key=valid_api_key)

        @test_app.get("/protected")
        async def protected_route(
            cred: tuple[str | None, MetaInfo | None] = Depends(require_credentials),
        ):
            _ = cred  # Dependency injection test
            return {"status": "ok"}

        client = TestClient(test_app)
        response = client.get("/protected", headers={"Authorization": f"Bearer {valid_api_key}"})
        assert response.status_code == 401
        assert "Valid credential required" in response.json()["detail"]

    def test_require_credentials_with_only_meta_info(self, test_app, valid_api_key, valid_meta_jwt):
        """Test require_credentials with only MetaInfo (missing API key)."""
        test_app.add_middleware(CredentialMiddleware, api_key=valid_api_key)

        @test_app.get("/protected")
        async def protected_route(
            cred: tuple[str | None, MetaInfo | None] = Depends(require_credentials),
        ):
            _ = cred  # Dependency injection test
            return {"status": "ok"}

        client = TestClient(test_app)
        response = client.get("/protected", headers={"X-META-INFO": valid_meta_jwt})
        assert response.status_code == 401
        assert "Valid credential required" in response.json()["detail"]

    def test_require_credentials_with_invalid_api_key(
        self, test_app, valid_api_key, valid_meta_jwt
    ):
        """Test require_credentials with invalid API key."""
        test_app.add_middleware(CredentialMiddleware, api_key=valid_api_key)

        @test_app.get("/protected")
        async def protected_route(
            cred: tuple[str | None, MetaInfo | None] = Depends(require_credentials),
        ):
            _ = cred  # Dependency injection test
            return {"status": "ok"}

        client = TestClient(test_app)
        response = client.get(
            "/protected",
            headers={
                "Authorization": "Bearer wrong-key",
                "X-META-INFO": valid_meta_jwt,
            },
        )
        assert response.status_code == 401
        assert "Credential validation failed" in response.json()["detail"]
        assert "API key: Invalid API key" in response.json()["detail"]

    def test_require_credentials_with_invalid_meta_info(self, test_app, valid_api_key):
        """Test require_credentials with invalid MetaInfo."""
        test_app.add_middleware(CredentialMiddleware, api_key=valid_api_key)

        @test_app.get("/protected")
        async def protected_route(
            cred: tuple[str | None, MetaInfo | None] = Depends(require_credentials),
        ):
            _ = cred  # Dependency injection test
            return {"status": "ok"}

        client = TestClient(test_app)
        response = client.get(
            "/protected",
            headers={
                "Authorization": f"Bearer {valid_api_key}",
                "X-META-INFO": "invalid-jwt",
            },
        )
        assert response.status_code == 401
        assert "Credential validation failed" in response.json()["detail"]
        assert "MetaInfo: Invalid MetaInfo JWT" in response.json()["detail"]

    def test_require_credentials_with_both_invalid(self, test_app, valid_api_key):
        """Test require_credentials with both credentials invalid."""
        test_app.add_middleware(CredentialMiddleware, api_key=valid_api_key)

        @test_app.get("/protected")
        async def protected_route(
            cred: tuple[str | None, MetaInfo | None] = Depends(require_credentials),
        ):
            _ = cred  # Dependency injection test
            return {"status": "ok"}

        client = TestClient(test_app)
        response = client.get(
            "/protected",
            headers={
                "Authorization": "Bearer wrong-key",
                "X-META-INFO": "invalid-jwt",
            },
        )
        assert response.status_code == 401
        detail = response.json()["detail"]
        assert "Credential validation failed" in detail
        assert "API key: Invalid API key" in detail
        assert "MetaInfo: Invalid MetaInfo JWT" in detail


class TestRequireCredentialsCustomRealm:
    """Test RequireCredentials with custom realm."""

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

        @test_app.get("/admin")
        async def admin_route(
            cred: tuple[str | None, MetaInfo | None] = Depends(RequireCredentials("Admin area")),
        ):
            _ = cred  # Dependency injection test
            return {"status": "ok"}

        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_credentials(self, test_app, valid_api_key, valid_meta_jwt):
        """Test custom realm with valid credentials."""
        test_app.add_middleware(CredentialMiddleware, api_key=valid_api_key)

        @test_app.get("/admin")
        async def admin_route(
            cred: tuple[str | None, MetaInfo | None] = Depends(RequireCredentials("Admin area")),
        ):
            api_key, meta_info = cred
            return {"has_access": True}

        client = TestClient(test_app)
        response = client.get(
            "/admin",
            headers={
                "Authorization": f"Bearer {valid_api_key}",
                "X-META-INFO": valid_meta_jwt,
            },
        )
        assert response.status_code == 200
        assert response.json()["has_access"] is True


class TestGetCredentialsDefault:
    """Test default get_credentials instance."""

    def test_get_credentials_with_both_valid(self, test_app, valid_api_key, valid_meta_jwt):
        """Test get_credentials with both valid credentials."""
        test_app.add_middleware(CredentialMiddleware, api_key=valid_api_key)

        @test_app.get("/optional")
        async def optional_route(
            cred: tuple[str | None, MetaInfo | None] = Depends(get_credentials),
        ):
            api_key, meta_info = cred
            return {
                "has_api_key": api_key is not None,
                "has_meta_info": meta_info is not None,
            }

        client = TestClient(test_app)
        response = client.get(
            "/optional",
            headers={
                "Authorization": f"Bearer {valid_api_key}",
                "X-META-INFO": valid_meta_jwt,
            },
        )
        assert response.status_code == 200
        data = response.json()
        assert data["has_api_key"] is True
        assert data["has_meta_info"] is True

    def test_get_credentials_without_any(self, test_app, valid_api_key):
        """Test get_credentials without any credentials."""
        test_app.add_middleware(CredentialMiddleware, api_key=valid_api_key)

        @test_app.get("/optional")
        async def optional_route(
            cred: tuple[str | None, MetaInfo | None] = Depends(get_credentials),
        ):
            api_key, meta_info = cred
            return {
                "has_api_key": api_key is not None,
                "has_meta_info": meta_info is not None,
            }

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

    def test_get_credentials_with_only_api_key(self, test_app, valid_api_key):
        """Test get_credentials with only API key."""
        test_app.add_middleware(CredentialMiddleware, api_key=valid_api_key)

        @test_app.get("/optional")
        async def optional_route(
            cred: tuple[str | None, MetaInfo | None] = Depends(get_credentials),
        ):
            api_key, meta_info = cred
            return {
                "has_api_key": api_key is not None,
                "has_meta_info": meta_info is not None,
            }

        client = TestClient(test_app)
        response = client.get("/optional", headers={"Authorization": f"Bearer {valid_api_key}"})
        assert response.status_code == 200
        data = response.json()
        assert data["has_api_key"] is True
        assert data["has_meta_info"] is False

    def test_get_credentials_with_only_meta_info(self, test_app, valid_api_key, valid_meta_jwt):
        """Test get_credentials with only MetaInfo."""
        test_app.add_middleware(CredentialMiddleware, api_key=valid_api_key)

        @test_app.get("/optional")
        async def optional_route(
            cred: tuple[str | None, MetaInfo | None] = Depends(get_credentials),
        ):
            api_key, meta_info = cred
            return {
                "has_api_key": api_key is not None,
                "has_meta_info": meta_info is not None,
            }

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

    def test_get_credentials_with_invalid_credentials(self, test_app, valid_api_key):
        """Test get_credentials with invalid credentials returns None."""
        test_app.add_middleware(CredentialMiddleware, api_key=valid_api_key)

        @test_app.get("/optional")
        async def optional_route(
            cred: tuple[str | None, MetaInfo | None] = Depends(get_credentials),
        ):
            api_key, meta_info = cred
            return {
                "has_api_key": api_key is not None,
                "has_meta_info": meta_info is not None,
            }

        client = TestClient(test_app)
        response = client.get(
            "/optional",
            headers={
                "Authorization": "Bearer wrong-key",
                "X-META-INFO": "invalid-jwt",
            },
        )
        assert response.status_code == 200
        data = response.json()
        assert data["has_api_key"] is False
        assert data["has_meta_info"] is False


class TestGetCredentialsClass:
    """Test GetCredentials class."""

    def test_get_credentials_class_with_both(self, test_app, valid_api_key, valid_meta_jwt):
        """Test GetCredentials class with both credentials."""
        test_app.add_middleware(CredentialMiddleware, api_key=valid_api_key)

        @test_app.get("/optional")
        async def optional_route(
            cred: tuple[str | None, MetaInfo | None] = Depends(GetCredentials()),
        ):
            api_key, meta_info = cred
            return {
                "has_api_key": api_key is not None,
                "has_meta_info": meta_info is not None,
            }

        client = TestClient(test_app)
        response = client.get(
            "/optional",
            headers={
                "Authorization": f"Bearer {valid_api_key}",
                "X-META-INFO": valid_meta_jwt,
            },
        )
        assert response.status_code == 200
        data = response.json()
        assert data["has_api_key"] is True
        assert data["has_meta_info"] is True


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

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

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

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

    def test_get_credentials_callable(self):
        """Test GetCredentials is callable."""
        instance = GetCredentials()
        assert callable(instance)

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

        @test_app.get("/protected")
        async def protected_route(
            cred: tuple[str | None, MetaInfo | None] = Depends(require_credentials),
        ):
            _ = cred  # Dependency injection test
            return {"status": "ok"}

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

    def test_tuple_unpacking(self, test_app, valid_api_key, valid_meta_jwt):
        """Test tuple unpacking of credentials."""
        test_app.add_middleware(CredentialMiddleware, api_key=valid_api_key)

        @test_app.get("/protected")
        async def protected_route(
            cred: tuple[str | None, MetaInfo | None] = Depends(require_credentials),
        ):
            api_key, meta_info = cred
            assert isinstance(api_key, str)
            assert isinstance(meta_info, MetaInfo)
            return {
                "api_key_length": len(api_key),
                "subject": meta_info.subject,
            }

        client = TestClient(test_app)
        response = client.get(
            "/protected",
            headers={
                "Authorization": f"Bearer {valid_api_key}",
                "X-META-INFO": valid_meta_jwt,
            },
        )
        assert response.status_code == 200
        data = response.json()
        assert data["api_key_length"] == len(valid_api_key)
        assert data["subject"] == "测试主体"
