"""Integration tests for MCP Oauth Protected Resource."""

from urllib.parse import urlparse

import httpx
import pytest
from inline_snapshot import snapshot
from pydantic import AnyHttpUrl
from starlette.applications import Starlette

from mcp.server.auth.routes import build_resource_metadata_url, create_protected_resource_routes


@pytest.fixture
def test_app():
    """Fixture to create protected resource routes for testing."""

    # Create the protected resource routes
    protected_resource_routes = create_protected_resource_routes(
        resource_url=AnyHttpUrl("https://example.com/resource"),
        authorization_servers=[AnyHttpUrl("https://auth.example.com/authorization")],
        scopes_supported=["read", "write"],
        resource_name="Example Resource",
        resource_documentation=AnyHttpUrl("https://docs.example.com/resource"),
    )

    app = Starlette(routes=protected_resource_routes)
    return app


@pytest.fixture
async def test_client(test_app: Starlette):
    """Fixture to create an HTTP client for the protected resource app."""
    async with httpx.AsyncClient(transport=httpx.ASGITransport(app=test_app), base_url="https://mcptest.com") as client:
        yield client


@pytest.mark.anyio
async def test_metadata_endpoint_with_path(test_client: httpx.AsyncClient):
    """Test the OAuth 2.0 Protected Resource metadata endpoint for path-based resource."""

    # For resource with path "/resource", metadata should be accessible at the path-aware location
    response = await test_client.get("/.well-known/oauth-protected-resource/resource")
    assert response.json() == snapshot(
        {
            "resource": "https://example.com/resource",
            "authorization_servers": ["https://auth.example.com/authorization"],
            "scopes_supported": ["read", "write"],
            "resource_name": "Example Resource",
            "resource_documentation": "https://docs.example.com/resource",
            "bearer_methods_supported": ["header"],
        }
    )


@pytest.mark.anyio
async def test_metadata_endpoint_root_path_returns_404(test_client: httpx.AsyncClient):
    """Test that root path returns 404 for path-based resource."""

    # Root path should return 404 for path-based resources
    response = await test_client.get("/.well-known/oauth-protected-resource")
    assert response.status_code == 404


@pytest.fixture
def root_resource_app():
    """Fixture to create protected resource routes for root-level resource."""

    # Create routes for a resource without path component
    protected_resource_routes = create_protected_resource_routes(
        resource_url=AnyHttpUrl("https://example.com"),
        authorization_servers=[AnyHttpUrl("https://auth.example.com")],
        scopes_supported=["read"],
        resource_name="Root Resource",
    )

    app = Starlette(routes=protected_resource_routes)
    return app


@pytest.fixture
async def root_resource_client(root_resource_app: Starlette):
    """Fixture to create an HTTP client for the root resource app."""
    async with httpx.AsyncClient(
        transport=httpx.ASGITransport(app=root_resource_app), base_url="https://mcptest.com"
    ) as client:
        yield client


@pytest.mark.anyio
async def test_metadata_endpoint_without_path(root_resource_client: httpx.AsyncClient):
    """Test metadata endpoint for root-level resource."""

    # For root resource, metadata should be at standard location
    response = await root_resource_client.get("/.well-known/oauth-protected-resource")
    assert response.status_code == 200
    assert response.json() == snapshot(
        {
            "resource": "https://example.com/",
            "authorization_servers": ["https://auth.example.com/"],
            "scopes_supported": ["read"],
            "resource_name": "Root Resource",
            "bearer_methods_supported": ["header"],
        }
    )


# Tests for URL construction utility function


def test_metadata_url_construction_url_without_path():
    """Test URL construction for resource without path component."""
    resource_url = AnyHttpUrl("https://example.com")
    result = build_resource_metadata_url(resource_url)
    assert str(result) == "https://example.com/.well-known/oauth-protected-resource"


def test_metadata_url_construction_url_with_path_component():
    """Test URL construction for resource with path component."""
    resource_url = AnyHttpUrl("https://example.com/mcp")
    result = build_resource_metadata_url(resource_url)
    assert str(result) == "https://example.com/.well-known/oauth-protected-resource/mcp"


def test_metadata_url_construction_url_with_trailing_slash_only():
    """Test URL construction for resource with trailing slash only."""
    resource_url = AnyHttpUrl("https://example.com/")
    result = build_resource_metadata_url(resource_url)
    # Trailing slash should be treated as empty path
    assert str(result) == "https://example.com/.well-known/oauth-protected-resource"


@pytest.mark.parametrize(
    "resource_url,expected_url",
    [
        ("https://example.com", "https://example.com/.well-known/oauth-protected-resource"),
        ("https://example.com/", "https://example.com/.well-known/oauth-protected-resource"),
        ("https://example.com/mcp", "https://example.com/.well-known/oauth-protected-resource/mcp"),
        ("http://localhost:8001/mcp", "http://localhost:8001/.well-known/oauth-protected-resource/mcp"),
    ],
)
def test_metadata_url_construction_various_resource_configurations(resource_url: str, expected_url: str):
    """Test URL construction with various resource configurations."""
    result = build_resource_metadata_url(AnyHttpUrl(resource_url))
    assert str(result) == expected_url


# Tests for consistency between URL generation and route registration


def test_route_consistency_route_path_matches_metadata_url():
    """Test that route path matches the generated metadata URL."""
    resource_url = AnyHttpUrl("https://example.com/mcp")

    # Generate metadata URL
    metadata_url = build_resource_metadata_url(resource_url)

    # Create routes
    routes = create_protected_resource_routes(
        resource_url=resource_url,
        authorization_servers=[AnyHttpUrl("https://auth.example.com")],
    )

    # Extract path from metadata URL
    metadata_path = urlparse(str(metadata_url)).path

    # Verify consistency
    assert len(routes) == 1
    assert routes[0].path == metadata_path


@pytest.mark.parametrize(
    "resource_url,expected_path",
    [
        ("https://example.com", "/.well-known/oauth-protected-resource"),
        ("https://example.com/", "/.well-known/oauth-protected-resource"),
        ("https://example.com/mcp", "/.well-known/oauth-protected-resource/mcp"),
    ],
)
def test_route_consistency_consistent_paths_for_various_resources(resource_url: str, expected_path: str):
    """Test that URL generation and route creation are consistent."""
    resource_url_obj = AnyHttpUrl(resource_url)

    # Test URL generation
    metadata_url = build_resource_metadata_url(resource_url_obj)
    url_path = urlparse(str(metadata_url)).path

    # Test route creation
    routes = create_protected_resource_routes(
        resource_url=resource_url_obj,
        authorization_servers=[AnyHttpUrl("https://auth.example.com")],
    )
    route_path = routes[0].path

    # Both should match expected path
    assert url_path == expected_path
    assert route_path == expected_path
    assert url_path == route_path
