from __future__ import annotations

import json
import os
import sys
from typing import Any, TextIO

from grantflow.mcp.tools import DEFAULT_BASE_URL, GrantFlowToolClient, GrantFlowToolError, list_tools


def handle_request(request: dict[str, Any], client: GrantFlowToolClient) -> dict[str, Any]:
    request_id = request.get("id")
    method = request.get("method")
    try:
        if method == "tools/list":
            return _success(request_id, {"tools": list_tools()})
        if method == "tools/call":
            params = request.get("params") or {}
            name = params.get("name")
            if not isinstance(name, str):
                raise GrantFlowToolError(
                    "Missing tool name",
                    detail={
                        "code": "missing_tool_name",
                        "message": "tools/call requires params.name.",
                        "retryable": False,
                    },
                )
            arguments = params.get("arguments") or {}
            if not isinstance(arguments, dict):
                raise GrantFlowToolError(
                    "Invalid tool arguments",
                    detail={
                        "code": "invalid_tool_arguments",
                        "message": "tools/call params.arguments must be an object.",
                        "retryable": False,
                    },
                )
            result = client.call_tool(name, arguments)
            return _success(request_id, {"content": [{"type": "json", "json": result}], "isError": False})
        raise GrantFlowToolError(
            "Unknown method",
            detail={
                "code": "unknown_method",
                "message": f"Unknown method: {method}",
                "retryable": False,
                "available_methods": ["tools/list", "tools/call"],
            },
        )
    except GrantFlowToolError as exc:
        return _success(request_id, {"content": [{"type": "json", "json": exc.detail}], "isError": True})
    except Exception as exc:  # noqa: BLE001
        return _success(
            request_id,
            {
                "content": [
                    {
                        "type": "json",
                        "json": {
                            "code": "tool_server_error",
                            "message": str(exc),
                            "retryable": False,
                        },
                    }
                ],
                "isError": True,
            },
        )


def serve(input_stream: TextIO = sys.stdin, output_stream: TextIO = sys.stdout) -> None:
    base_url = os.getenv("GRANTFLOW_BASE_URL", DEFAULT_BASE_URL)
    api_key = os.getenv("GRANTFLOW_API_KEY") or None
    with GrantFlowToolClient(base_url=base_url, api_key=api_key) as client:
        for raw_line in input_stream:
            line = raw_line.strip()
            if not line:
                continue
            try:
                request = json.loads(line)
                if not isinstance(request, dict):
                    raise ValueError("request must be an object")
                response = handle_request(request, client)
            except Exception as exc:  # noqa: BLE001
                response = _success(
                    None,
                    {
                        "content": [
                            {
                                "type": "json",
                                "json": {
                                    "code": "invalid_server_request",
                                    "message": str(exc),
                                    "retryable": False,
                                },
                            }
                        ],
                        "isError": True,
                    },
                )
            output_stream.write(json.dumps(response, separators=(",", ":")) + "\n")
            output_stream.flush()


def _success(request_id: Any, result: dict[str, Any]) -> dict[str, Any]:
    return {"jsonrpc": "2.0", "id": request_id, "result": result}


def main() -> None:
    serve()


if __name__ == "__main__":
    main()
