"""Tests for OAuth 2.0 shared code."""

import pytest
from pydantic import ValidationError

from mcp.shared.auth import OAuthClientInformationFull, OAuthClientMetadata, OAuthMetadata


def test_oauth():
    """Should not throw when parsing OAuth metadata."""
    OAuthMetadata.model_validate(
        {
            "issuer": "https://example.com",
            "authorization_endpoint": "https://example.com/oauth2/authorize",
            "token_endpoint": "https://example.com/oauth2/token",
            "scopes_supported": ["read", "write"],
            "response_types_supported": ["code", "token"],
            "token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"],
        }
    )


def test_oidc():
    """Should not throw when parsing OIDC metadata."""
    OAuthMetadata.model_validate(
        {
            "issuer": "https://example.com",
            "authorization_endpoint": "https://example.com/oauth2/authorize",
            "token_endpoint": "https://example.com/oauth2/token",
            "end_session_endpoint": "https://example.com/logout",
            "id_token_signing_alg_values_supported": ["RS256"],
            "jwks_uri": "https://example.com/.well-known/jwks.json",
            "response_types_supported": ["code", "token"],
            "revocation_endpoint": "https://example.com/oauth2/revoke",
            "scopes_supported": ["openid", "read", "write"],
            "subject_types_supported": ["public"],
            "token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"],
            "userinfo_endpoint": "https://example.com/oauth2/userInfo",
        }
    )


def test_oauth_with_jarm():
    """Should not throw when parsing OAuth metadata that includes JARM response modes."""
    OAuthMetadata.model_validate(
        {
            "issuer": "https://example.com",
            "authorization_endpoint": "https://example.com/oauth2/authorize",
            "token_endpoint": "https://example.com/oauth2/token",
            "scopes_supported": ["read", "write"],
            "response_types_supported": ["code", "token"],
            "response_modes_supported": [
                "query",
                "fragment",
                "form_post",
                "query.jwt",
                "fragment.jwt",
                "form_post.jwt",
                "jwt",
            ],
            "token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"],
        }
    )


# RFC 7591 §2 marks client_uri/logo_uri/tos_uri/policy_uri/jwks_uri as OPTIONAL.
# Some authorization servers echo the client's omitted metadata back as ""
# instead of dropping the keys; without coercion, AnyHttpUrl rejects "" and
# the whole registration response is thrown away even though the server
# returned a valid client_id.


@pytest.mark.parametrize(
    "empty_field",
    ["client_uri", "logo_uri", "tos_uri", "policy_uri", "jwks_uri"],
)
def test_optional_url_empty_string_coerced_to_none(empty_field: str):
    data = {
        "redirect_uris": ["https://example.com/callback"],
        empty_field: "",
    }
    metadata = OAuthClientMetadata.model_validate(data)
    assert getattr(metadata, empty_field) is None


def test_all_optional_urls_empty_together():
    data = {
        "redirect_uris": ["https://example.com/callback"],
        "client_uri": "",
        "logo_uri": "",
        "tos_uri": "",
        "policy_uri": "",
        "jwks_uri": "",
    }
    metadata = OAuthClientMetadata.model_validate(data)
    assert metadata.client_uri is None
    assert metadata.logo_uri is None
    assert metadata.tos_uri is None
    assert metadata.policy_uri is None
    assert metadata.jwks_uri is None


def test_valid_url_passes_through_unchanged():
    data = {
        "redirect_uris": ["https://example.com/callback"],
        "client_uri": "https://udemy.com/",
    }
    metadata = OAuthClientMetadata.model_validate(data)
    assert str(metadata.client_uri) == "https://udemy.com/"


def test_information_full_inherits_coercion():
    """OAuthClientInformationFull subclasses OAuthClientMetadata, so the
    same coercion applies to DCR responses parsed via the full model."""
    data = {
        "client_id": "abc123",
        "redirect_uris": ["https://example.com/callback"],
        "client_uri": "",
        "logo_uri": "",
        "tos_uri": "",
        "policy_uri": "",
        "jwks_uri": "",
    }
    info = OAuthClientInformationFull.model_validate(data)
    assert info.client_id == "abc123"
    assert info.client_uri is None
    assert info.logo_uri is None
    assert info.tos_uri is None
    assert info.policy_uri is None
    assert info.jwks_uri is None


def test_invalid_non_empty_url_still_rejected():
    """Coercion must only touch empty strings — garbage URLs still raise."""
    data = {
        "redirect_uris": ["https://example.com/callback"],
        "client_uri": "not a url",
    }
    with pytest.raises(ValidationError):
        OAuthClientMetadata.model_validate(data)
