# SPDX-License-Identifier: GPL-3.0-or-later
import pytest
from mcp.shared.memory import create_connected_server_and_client_session

from mcp_nix import mcp
from mcp_nix import tools as _  # noqa: F401 - registers tools

pytestmark = pytest.mark.anyio


async def test_list_tools():
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        tools = await client.list_tools()
        tool_names = [t.name for t in tools.tools]
        assert "search_nixpkgs" in tool_names
        assert "search_nixos_options" in tool_names
        assert "list_nixos_channels" in tool_names


async def test_search_package():
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("search_nixpkgs", {"query": "git"})
        assert result.content
        text = result.content[0].text
        assert "git" in text.lower()


async def test_search_option():
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("search_nixos_options", {"query": "nginx"})
        assert result.content
        text = result.content[0].text
        assert "nginx" in text.lower()


async def test_channels():
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("list_nixos_channels", {})
        assert result.content
        text = result.content[0].text
        assert "unstable" in text.lower()


async def test_get_package_details():
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("show_nixpkgs_package", {"name": "git"})
        assert result.content
        text = result.content[0].text
        assert "git" in text.lower()


async def test_get_option_details():
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("show_nixos_option", {"name": "services.nginx.enable"})
        assert result.content
        text = result.content[0].text
        assert "nginx" in text.lower()


async def test_homemanager_search_option():
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("search_homemanager_options", {"query": "git"})
        assert result.content
        text = result.content[0].text
        assert "git" in text.lower()


async def test_homemanager_get_option_details():
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("show_homemanager_option", {"name": "programs.git.enable"})
        assert result.content
        text = result.content[0].text
        assert "git" in text.lower()


async def test_homemanager_releases():
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("list_homemanager_releases", {})
        assert result.content
        text = result.content[0].text
        assert "unstable" in text.lower()


# Tests for prefix/children behavior - prevents regression
async def test_nixos_option_prefix_returns_children():
    """Prefix like 'services.nginx' should return child options, not error."""
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("show_nixos_option", {"name": "services.nginx"})
        assert result.content
        text = result.content[0].text
        # Should list children, not return "not found"
        assert "child options" in text.lower()
        assert "services.nginx." in text  # Should have actual child options


async def test_homemanager_option_prefix_returns_children():
    """Prefix like 'programs.git' should return child options, not error."""
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("show_homemanager_option", {"name": "programs.git"})
        assert result.content
        text = result.content[0].text
        # Should list children, not return "not found"
        assert "child options" in text.lower()
        assert "programs.git." in text  # Should have actual child options


async def test_nixos_option_leaf_returns_details():
    """Leaf option like 'services.nginx.enable' should return full details."""
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("show_nixos_option", {"name": "services.nginx.enable"})
        assert result.content
        text = result.content[0].text
        # Should have option details, not children list
        assert "type:" in text.lower()
        assert "child options" not in text.lower()


async def test_homemanager_option_leaf_returns_details():
    """Leaf option like 'programs.git.enable' should return full details."""
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("show_homemanager_option", {"name": "programs.git.enable"})
        assert result.content
        text = result.content[0].text
        # Should have option details, not children list
        assert "type:" in text.lower()
        assert "child options" not in text.lower()


async def test_nixvim_search_option():
    """Search NixVim options."""
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("search_nixvim_options", {"query": "colorscheme"})
        assert result.content
        text = result.content[0].text
        assert "colorscheme" in text.lower()


async def test_nixvim_get_option_details():
    """Get NixVim option details."""
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("show_nixvim_option", {"name": "colorscheme"})
        assert result.content
        text = result.content[0].text
        assert "colorscheme" in text.lower()
        assert "type:" in text.lower()


async def test_nixhub_list_package_versions():
    """List available versions for a package."""
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("list_package_versions", {"name": "nodejs"})
        assert result.content
        text = result.content[0].text
        assert "versions" in text.lower()


async def test_nixhub_get_commit():
    """Get nixpkgs commit for a specific version."""
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool(
            "find_nixpkgs_commit_with_package_version", {"name": "nodejs", "version": "20.11.0"}
        )
        assert result.content
        text = result.content[0].text
        # It's unlikely nodejs 20.11.0 would change, and if it does, its trivially updatable.
        assert "10b813040df67c4039086db0f6eaf65c536886c6" in text


async def test_nixhub_package_not_found():
    """Non-existent package should return error."""
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("list_package_versions", {"name": "nonexistent-package-xyz123"})
        assert result.content
        text = result.content[0].text
        assert "error" in text.lower() or "not found" in text.lower()


# Declaration tools tests
async def test_read_derivation():
    """Read derivation source for a package."""
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("read_derivation", {"name": "git"})
        assert result.content
        text = result.content[0].text
        assert "Reference:" in text
        assert "Source:" in text
        assert "lines" in text
        assert "stdenv" in text or "mkDerivation" in text


async def test_read_nixos_module():
    """Read NixOS module source for an option."""
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("read_nixos_module", {"name": "services.nginx.enable"})
        assert result.content
        text = result.content[0].text
        assert "Reference:" in text
        assert "Source:" in text
        assert "lines" in text
        assert "nginx" in text.lower()


async def test_read_home_module():
    """Read Home Manager module source for an option."""
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("read_home_module", {"name": "programs.git.enable"})
        assert result.content
        text = result.content[0].text
        assert "Reference:" in text
        assert "Source:" in text
        assert "lines" in text
        assert "git" in text.lower()


async def test_read_nixvim_declaration():
    """Read NixVim declaration source for an option."""
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("read_nixvim_declaration", {"name": "colorscheme"})
        assert result.content
        text = result.content[0].text
        assert "Reference:" in text
        assert "Source:" in text
        assert "lines" in text


async def test_read_nix_darwin_declaration():
    """Read nix-darwin declaration source for an option."""
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("read_nix_darwin_declaration", {"name": "system.defaults.dock.autohide"})
        assert result.content
        text = result.content[0].text
        assert "Reference:" in text
        assert "Source:" in text
        assert "lines" in text


async def test_show_nixpkgs_package_has_reference():
    """show_nixpkgs_package should include Reference with line count."""
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("show_nixpkgs_package", {"name": "git"})
        assert result.content
        text = result.content[0].text
        assert "Reference:" in text
        assert "lines" in text
        assert "read_derivation" in text


async def test_show_nixos_option_has_reference():
    """show_nixos_option should include Reference with line count."""
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("show_nixos_option", {"name": "services.nginx.enable"})
        assert result.content
        text = result.content[0].text
        assert "Reference:" in text
        assert "lines" in text
        assert "read_nixos_module" in text


async def test_show_homemanager_option_has_reference():
    """show_homemanager_option should include Reference with line count."""
    async with create_connected_server_and_client_session(mcp._mcp_server) as client:
        result = await client.call_tool("show_homemanager_option", {"name": "programs.git.enable"})
        assert result.content
        text = result.content[0].text
        assert "Reference:" in text
        assert "lines" in text
        assert "read_home_module" in text
