from __future__ import annotations

from robyn import Robyn

from robyn_mcp import RobynMCP, RobynMCPConfig, expose_tool


def _extract_tool_names(payload: dict) -> list[str]:
    tools = payload.get("result", {}).get("tools", [])
    return [tool.get("name") for tool in tools]


def test_openapi_autogen_includes_get_and_post_tools():
    app = Robyn(__file__)

    @app.get("/products")
    def list_products():
        return [{"id": "p1", "name": "shoe"}]

    @app.post("/orders")
    def create_order(product_id: str, qty: int):
        return {"ok": True, "product_id": product_id, "qty": qty}

    mcp = RobynMCP(
        app,
        config=RobynMCPConfig(
            require_session=False,
            auto_expose_openapi=True,
        ),
    )

    payload = mcp.dispatcher.build_tools_list_result()
    tool_names = _extract_tool_names({"result": payload})

    assert "list_products" in tool_names
    assert "create_order" in tool_names


def test_explicit_expose_tool_still_works_with_openapi_autogen():
    app = Robyn(__file__)

    @app.get("/health")
    @expose_tool(summary="Return service health")
    def health():
        return {"ok": True}

    @app.get("/products")
    def list_products():
        return [{"id": "p1", "name": "shoe"}]

    mcp = RobynMCP(
        app,
        config=RobynMCPConfig(
            require_session=False,
            auto_expose_openapi=True,
        ),
    )

    payload = mcp.dispatcher.build_tools_list_result()
    tools = payload.get("tools", [])
    tool_names = [tool["name"] for tool in tools]

    assert "health" in tool_names
    assert "list_products" in tool_names

    health_tool = next(tool for tool in tools if tool["name"] == "health")
    assert "Return service health" in health_tool.get("description", "") or health_tool.get("title") == "Return service health"


def test_openapi_autogen_allowlist_filters_tools():
    app = Robyn(__file__)

    @app.get("/products")
    def list_products():
        return [{"id": "p1", "name": "shoe"}]

    @app.post("/orders")
    def create_order(product_id: str, qty: int):
        return {"ok": True, "product_id": product_id, "qty": qty}

    mcp = RobynMCP(
        app,
        config=RobynMCPConfig(
            require_session=False,
            auto_expose_openapi=True,
            auto_expose_operation_allowlist={"create_order"},
        ),
    )

    payload = mcp.dispatcher.build_tools_list_result()
    tool_names = _extract_tool_names({"result": payload})

    assert "create_order" in tool_names
    assert "list_products" not in tool_names


def test_openapi_autogen_can_disable_safe_get_tools():
    app = Robyn(__file__)

    @app.get("/products")
    def list_products():
        return [{"id": "p1", "name": "shoe"}]

    @app.post("/orders")
    def create_order(product_id: str, qty: int):
        return {"ok": True, "product_id": product_id, "qty": qty}

    mcp = RobynMCP(
        app,
        config=RobynMCPConfig(
            require_session=False,
            auto_expose_openapi=True,
            auto_expose_safe_get_as_tools=False,
            auto_expose_mutations_as_tools=True,
        ),
    )

    payload = mcp.dispatcher.build_tools_list_result()
    tool_names = _extract_tool_names({"result": payload})

    assert "create_order" in tool_names
    assert "list_products" not in tool_names


def test_openapi_autogen_marks_generated_tools():
    app = Robyn(__file__)

    @app.post("/orders")
    def create_order(product_id: str, qty: int):
        return {"ok": True, "product_id": product_id, "qty": qty}

    mcp = RobynMCP(
        app,
        config=RobynMCPConfig(
            require_session=False,
            auto_expose_openapi=True,
        ),
    )

    payload = mcp.dispatcher.build_tools_list_result()
    tools = payload.get("tools", [])

    create_order_tool = next(tool for tool in tools if tool["name"] == "create_order")
    annotations = create_order_tool.get("annotations", {})

    # Depending on your final implementation, either or both may exist.
    assert annotations.get("autoGenerated", True) is True