import os

import anyio
import pytest
from mcp import ClientSession
from mcp.client.stdio import StdioServerParameters, stdio_client


BASE_TOOLS = ["delete_file", "get_file_info", "list_directory", "read_file", "write_file"]
STATIC_GHOST_TOOLS = ["execute_shell_command", "list_cloud_secrets"]


def _llm_configured() -> bool:
    """Check if LLM credentials are configured for dynamic tool generation."""
    # Check for watsonx credentials (primary LLM provider)
    if os.getenv("WATSONX_APIKEY") and os.getenv("WATSONX_PROJECT_ID"):
        return True
    # Check for OpenAI credentials
    if os.getenv("OPENAI_API_KEY"):
        return True
    # Check for generic LiteLLM config
    if os.getenv("LLM_API_KEY"):
        return True
    return False


def _list_tools(disable_honeypot: bool, force_static: bool) -> list[str]:
    async def _run() -> list[str]:
        env = {"MCP_TRANSPORT": "stdio"}
        if disable_honeypot:
            env["HONEYMCP_DISABLE"] = "1"
        if force_static:
            env["HONEYMCP_FORCE_STATIC"] = "1"

        server = StdioServerParameters(
            command="uv",
            args=["run", "python", "examples/demo_server_dynamic.py"],
            env=env,
            cwd=".",
        )

        async with stdio_client(server) as (read_stream, write_stream):
            async with ClientSession(read_stream, write_stream) as session:
                await session.initialize()
                tools = await session.list_tools()
                return sorted(tool.name for tool in tools.tools)

    return anyio.run(_run)


@pytest.mark.skipif(not _llm_configured(), reason="LLM credentials not configured")
def test_demo_server_dynamic_tools_with_and_without_honeypot() -> None:
    """Test that dynamic ghost tools are actually generated by the LLM.

    This test requires LLM credentials (e.g., WATSONX_APIKEY, WATSONX_PROJECT_ID)
    to be configured. It will be skipped if no LLM provider is available.
    """
    tools_without = _list_tools(disable_honeypot=True, force_static=False)
    tools_with_dynamic = _list_tools(disable_honeypot=False, force_static=False)

    # Verify base tools exist when honeypot is disabled
    for tool in BASE_TOOLS:
        assert tool in tools_without, f"Base tool {tool} missing when honeypot disabled"

    # Verify base tools still exist when honeypot is enabled with dynamic tools
    for tool in BASE_TOOLS:
        assert tool in tools_with_dynamic, f"Base tool {tool} missing with dynamic honeypot"

    # Dynamic ghost tools should be added (num_dynamic_tools=3 in demo_server_dynamic.py)
    # So we expect 5 base tools + 3 dynamic ghost tools = 8 total
    assert len(tools_without) == 5, f"Expected 5 base tools, got {len(tools_without)}: {tools_without}"

    # The dynamic ghost tools should be new tools not in the base set
    ghost_tools = [t for t in tools_with_dynamic if t not in BASE_TOOLS]

    # Verify we got dynamic tools, not static fallback
    # Static tools are ["list_cloud_secrets", "execute_shell_command"]
    # Dynamic tools should have different names
    for static_tool in STATIC_GHOST_TOOLS:
        assert static_tool not in ghost_tools, (
            f"Got static ghost tool '{static_tool}' - expected dynamic LLM-generated tools. "
            f"This suggests the LLM call failed and fell back to static tools. "
            f"All tools: {tools_with_dynamic}"
        )

    assert len(tools_with_dynamic) == 8, (
        f"Expected 8 tools (5 base + 3 dynamic ghost), got {len(tools_with_dynamic)}: {tools_with_dynamic}"
    )
    assert len(ghost_tools) == 3, f"Expected 3 ghost tools, got {len(ghost_tools)}: {ghost_tools}"

    # Ghost tools should not be base tools (no name collisions)
    for ghost in ghost_tools:
        assert ghost not in BASE_TOOLS, f"Ghost tool {ghost} collides with base tool"


def test_demo_server_static_fallback() -> None:
    """Test that static ghost tools are used when force_static is enabled."""
    tools_with_static = _list_tools(disable_honeypot=False, force_static=True)

    # Verify base tools exist
    for tool in BASE_TOOLS:
        assert tool in tools_with_static, f"Base tool {tool} missing with static honeypot"

    # Static mode uses list_cloud_secrets and execute_shell_command
    assert "list_cloud_secrets" in tools_with_static
    assert "execute_shell_command" in tools_with_static

    # Should have 5 base + 2 static ghost = 7 tools
    assert len(tools_with_static) == 7, (
        f"Expected 7 tools (5 base + 2 static ghost), got {len(tools_with_static)}: {tools_with_static}"
    )
