import re

import httpx
import pytest
from pydantic import AnyHttpUrl

from fastmcp import FastMCP
from fastmcp.server.auth import RemoteAuthProvider, TokenVerifier
from fastmcp.server.auth.auth import AccessToken
from fastmcp.server.auth.providers.jwt import StaticTokenVerifier


class LegacyTokenVerifier(TokenVerifier):
    """Mimics custom verifiers that still call the old positional super().__init__."""

    def __init__(
        self,
        base_url: AnyHttpUrl | str | None = None,
        required_scopes: list[str] | None = None,
    ):
        super().__init__(base_url, required_scopes)

    async def verify_token(self, token: str) -> AccessToken | None:
        return None


class TestAuthProviderBase:
    """Test suite for base AuthProvider behaviors that apply to all auth providers."""

    def test_token_verifier_preserves_legacy_positional_required_scopes(self):
        """Legacy positional super().__init__(base_url, required_scopes) should keep working."""
        verifier = LegacyTokenVerifier("https://my-server.com", ["read"])

        assert verifier.base_url == AnyHttpUrl("https://my-server.com/")
        assert verifier.required_scopes == ["read"]
        assert verifier.resource_base_url is None

    @pytest.fixture
    def basic_remote_provider(self):
        """Basic RemoteAuthProvider fixture for testing base AuthProvider behaviors."""
        # Create a static token verifier with a test token
        tokens = {
            "test_token": {
                "client_id": "test-client",
                "scopes": ["read", "write"],
            }
        }
        token_verifier = StaticTokenVerifier(tokens=tokens)
        return RemoteAuthProvider(
            token_verifier=token_verifier,
            authorization_servers=[AnyHttpUrl("https://auth.example.com")],
            base_url="https://my-server.com",
        )

    async def test_www_authenticate_header_points_to_base_url(
        self, basic_remote_provider
    ):
        """Test that WWW-Authenticate header points to RFC 9728-compliant metadata URL.

        The WWW-Authenticate header includes the resource path per RFC 9728,
        so clients can discover where the metadata is actually registered.
        """
        mcp = FastMCP("test-server", auth=basic_remote_provider)
        # Mount MCP at a non-root path
        mcp_http_app = mcp.http_app(path="/api/v1/mcp")

        async with httpx.AsyncClient(
            transport=httpx.ASGITransport(app=mcp_http_app),
            base_url="https://my-server.com",
        ) as client:
            # Make unauthorized request to MCP endpoint
            response = await client.get("/api/v1/mcp")
            assert response.status_code == 401

            www_auth = response.headers.get("www-authenticate", "")
            assert "resource_metadata=" in www_auth

            # Extract the metadata URL from the header
            match = re.search(r'resource_metadata="([^"]+)"', www_auth)
            assert match is not None
            metadata_url = match.group(1)

            # The metadata URL includes the resource path per RFC 9728
            assert (
                metadata_url
                == "https://my-server.com/.well-known/oauth-protected-resource/api/v1/mcp"
            )

    async def test_automatic_resource_url_capture(self, basic_remote_provider):
        """Test that resource URL is automatically captured from MCP path.

        This test verifies PR #1682 functionality where the resource URL
        should be automatically set based on the MCP endpoint path.
        """
        mcp = FastMCP("test-server", auth=basic_remote_provider)
        # Mount MCP at a specific path
        mcp_http_app = mcp.http_app(path="/mcp")

        async with httpx.AsyncClient(
            transport=httpx.ASGITransport(app=mcp_http_app),
            base_url="https://my-server.com",
        ) as client:
            # The .well-known metadata is at a path-aware location per RFC 9728
            response = await client.get("/.well-known/oauth-protected-resource/mcp")
            assert response.status_code == 200

            data = response.json()
            # The resource URL should be automatically set to the MCP path
            assert data.get("resource") == "https://my-server.com/mcp"

    async def test_automatic_resource_url_with_nested_path(self, basic_remote_provider):
        """Test automatic resource URL capture with deeply nested MCP path."""
        mcp = FastMCP("test-server", auth=basic_remote_provider)
        mcp_http_app = mcp.http_app(path="/api/v2/services/mcp")

        async with httpx.AsyncClient(
            transport=httpx.ASGITransport(app=mcp_http_app),
            base_url="https://my-server.com",
        ) as client:
            # The .well-known metadata includes the resource path per RFC 9728
            response = await client.get(
                "/.well-known/oauth-protected-resource/api/v2/services/mcp"
            )
            assert response.status_code == 200

            data = response.json()
            # Should automatically capture the nested path
            assert data.get("resource") == "https://my-server.com/api/v2/services/mcp"
