"""Integration tests for MCP tools."""

import httpx
import respx
from conftest import (
    SAMPLE_DOCKER_SEARCH_RESPONSE,
    SAMPLE_MAVEN_SEARCH_RESPONSE,
    SAMPLE_PYTHON_SEARCH_RESPONSE,
)
from httpx import Response

from nexus_mcp.nexus_client import NexusCredentials
from nexus_mcp.tools.implementations import (
    get_docker_tags_impl,
    get_maven_versions_impl,
    get_python_versions_impl,
    list_docker_images_impl,
    search_maven_artifact_impl,
    search_python_package_impl,
)


# Helper to create test credentials
def make_creds(url: str = "https://nexus.example.com") -> NexusCredentials:
    """Create test credentials."""
    return NexusCredentials(
        url=url,
        username="user",
        password="pass",
    )


class TestMavenTools:
    """Tests for Maven-related MCP tools."""

    @respx.mock
    async def test_search_maven_artifact_success(self) -> None:
        """Successful Maven search should return artifacts."""
        respx.get("https://nexus.example.com/service/rest/v1/search").mock(
            return_value=Response(200, json=SAMPLE_MAVEN_SEARCH_RESPONSE)
        )

        result = await search_maven_artifact_impl(
            creds=make_creds(),
            group_id="com.example",
            artifact_id="artifact",
        )

        assert "error" not in result
        assert result["count"] == 2
        assert len(result["artifacts"]) == 2
        assert result["artifacts"][0]["group"] == "com.example"

    @respx.mock
    async def test_search_maven_artifact_requires_params(self) -> None:
        """Search without group_id or artifact_id should return error."""
        result = await search_maven_artifact_impl(
            creds=make_creds(),
        )

        assert "error" in result
        assert "group_id or artifact_id" in result["error"]

    @respx.mock
    async def test_search_maven_artifact_auth_error(self) -> None:
        """Auth failure should return error message."""
        respx.get("https://nexus.example.com/service/rest/v1/search").mock(
            return_value=Response(401, json={"message": "Unauthorized"})
        )

        result = await search_maven_artifact_impl(
            creds=make_creds(),
            group_id="com.example",
        )

        assert "error" in result
        assert "Authentication" in result["error"]

    @respx.mock
    async def test_get_maven_versions_success(self) -> None:
        """Get versions should return sorted version list."""
        respx.get("https://nexus.example.com/service/rest/v1/search").mock(
            return_value=Response(200, json=SAMPLE_MAVEN_SEARCH_RESPONSE)
        )

        result = await get_maven_versions_impl(
            creds=make_creds(),
            group_id="com.example",
            artifact_id="artifact",
        )

        assert "error" not in result
        assert result["groupId"] == "com.example"
        assert result["artifactId"] == "artifact"
        assert result["count"] == 2
        assert result["hasMore"] is False
        assert "continuationToken" not in result

    @respx.mock
    async def test_get_maven_versions_with_pagination(self) -> None:
        """Get versions should support pagination with continuation token."""
        # First page with continuation token
        page1_response = {
            "items": [
                {
                    "id": "1",
                    "repository": "maven-releases",
                    "format": "maven2",
                    "group": "com.example",
                    "name": "artifact",
                    "version": "1.0.0",
                    "assets": [],
                }
            ],
            "continuationToken": "page2token",
        }

        # Second page without continuation token
        page2_response = {
            "items": [
                {
                    "id": "2",
                    "repository": "maven-releases",
                    "format": "maven2",
                    "group": "com.example",
                    "name": "artifact",
                    "version": "2.0.0",
                    "assets": [],
                }
            ],
        }

        route = respx.get("https://nexus.example.com/service/rest/v1/search")
        route.side_effect = [
            Response(200, json=page1_response),
            Response(200, json=page2_response),
        ]

        # First page
        result1 = await get_maven_versions_impl(
            creds=make_creds(),
            group_id="com.example",
            artifact_id="artifact",
        )

        assert "error" not in result1
        assert result1["count"] == 1
        assert result1["hasMore"] is True
        assert result1["continuationToken"] == "page2token"
        assert result1["versions"][0]["version"] == "1.0.0"

        # Second page
        result2 = await get_maven_versions_impl(
            creds=make_creds(),
            group_id="com.example",
            artifact_id="artifact",
            continuation_token="page2token",
        )

        assert "error" not in result2
        assert result2["count"] == 1
        assert result2["hasMore"] is False
        assert result2["versions"][0]["version"] == "2.0.0"


class TestPythonTools:
    """Tests for Python/PyPI-related MCP tools."""

    @respx.mock
    async def test_search_python_package_success(self) -> None:
        """Successful Python search should return packages."""
        respx.get("https://nexus.example.com/service/rest/v1/search").mock(
            return_value=Response(200, json=SAMPLE_PYTHON_SEARCH_RESPONSE)
        )

        result = await search_python_package_impl(
            creds=make_creds(),
            name="requests",
        )

        assert "error" not in result
        assert result["count"] == 1
        assert result["packages"][0]["name"] == "requests"

    @respx.mock
    async def test_search_python_package_normalized_name(self) -> None:
        """Search should also try normalized package names."""
        # First call returns empty, second with normalized name returns result
        route = respx.get("https://nexus.example.com/service/rest/v1/search")
        route.side_effect = [
            Response(200, json={"items": [], "continuationToken": None}),
            Response(200, json=SAMPLE_PYTHON_SEARCH_RESPONSE),
        ]

        result = await search_python_package_impl(
            creds=make_creds(),
            name="my-package",  # Has hyphen, will try underscore too
        )

        assert "error" not in result
        # Should have found the package via normalized name
        assert result["count"] >= 0  # May or may not find depending on mock

    @respx.mock
    async def test_get_python_versions_success(self) -> None:
        """Get versions should return version list."""
        respx.get("https://nexus.example.com/service/rest/v1/search").mock(
            return_value=Response(200, json=SAMPLE_PYTHON_SEARCH_RESPONSE)
        )

        result = await get_python_versions_impl(
            creds=make_creds(),
            package_name="requests",
        )

        assert "error" not in result
        assert result["packageName"] == "requests"
        assert result["count"] == 1
        assert result["hasMore"] is False

    @respx.mock
    async def test_get_python_versions_with_pagination(self) -> None:
        """Get Python versions should support pagination."""
        page1_response = {
            "items": [
                {
                    "id": "1",
                    "repository": "pypi-hosted",
                    "format": "pypi",
                    "name": "requests",
                    "version": "2.28.0",
                    "assets": [],
                }
            ],
            "continuationToken": "next_page",
        }

        page2_response = {
            "items": [
                {
                    "id": "2",
                    "repository": "pypi-hosted",
                    "format": "pypi",
                    "name": "requests",
                    "version": "2.29.0",
                    "assets": [],
                }
            ],
        }

        route = respx.get("https://nexus.example.com/service/rest/v1/search")
        route.side_effect = [
            Response(200, json=page1_response),
            Response(200, json=page2_response),
        ]

        # First page
        result1 = await get_python_versions_impl(
            creds=make_creds(),
            package_name="requests",
        )

        assert result1["hasMore"] is True
        assert result1["continuationToken"] == "next_page"

        # Second page
        result2 = await get_python_versions_impl(
            creds=make_creds(),
            package_name="requests",
            continuation_token="next_page",
        )

        assert result2["hasMore"] is False


class TestDockerTools:
    """Tests for Docker-related MCP tools."""

    @respx.mock
    async def test_list_docker_images_success(self) -> None:
        """List images should return image list."""
        respx.get("https://nexus.example.com/service/rest/v1/search").mock(
            return_value=Response(200, json=SAMPLE_DOCKER_SEARCH_RESPONSE)
        )

        result = await list_docker_images_impl(
            creds=make_creds(),
            repository="docker-hosted",
        )

        assert "error" not in result
        assert result["repository"] == "docker-hosted"
        assert result["count"] == 1
        assert result["images"][0]["name"] == "my-app"
        assert "latest" in result["images"][0]["tags"]

    @respx.mock
    async def test_get_docker_tags_success(self) -> None:
        """Get tags should return tag list."""
        respx.get("https://nexus.example.com/service/rest/v1/search").mock(
            return_value=Response(200, json=SAMPLE_DOCKER_SEARCH_RESPONSE)
        )

        result = await get_docker_tags_impl(
            creds=make_creds(),
            repository="docker-hosted",
            image_name="my-app",
        )

        assert "error" not in result
        assert result["repository"] == "docker-hosted"
        assert result["imageName"] == "my-app"
        assert result["count"] == 1
        assert result["tags"][0]["tag"] == "latest"


class TestErrorHandling:
    """Tests for error handling across all tools."""

    @respx.mock
    async def test_connection_error(self) -> None:
        """Connection errors should be handled gracefully."""
        respx.get("https://nexus.example.com/service/rest/v1/search").mock(
            side_effect=httpx.ConnectError("Connection refused")
        )

        result = await search_maven_artifact_impl(
            creds=make_creds(),
            group_id="com.example",
        )

        # Should have an error but not crash
        assert "error" in result

    async def test_invalid_url(self) -> None:
        """Invalid URL should return error."""
        result = await search_maven_artifact_impl(
            creds=make_creds(url="not-a-valid-url"),
            group_id="com.example",
        )

        assert "error" in result
        assert "Invalid" in result["error"]
