import { Elysia } from "elysia";
import { getMcpTools } from "../lib/mcp_tool_convert";

var tools = [] as any[];
const OPENAPI_URL = process.env.BUN_PUBLIC_BASE_URL + "/docs/json";
const FILTER_TAG = "mcp";

if (!process.env.BUN_PUBLIC_BASE_URL) {
    throw new Error("BUN_PUBLIC_BASE_URL environment variable is not set");
}

// =====================
// MCP Protocol Types
// =====================
type JSONRPCRequest = {
    jsonrpc: "2.0";
    id: string | number;
    method: string;
    params?: any;
};

type JSONRPCResponse = {
    jsonrpc: "2.0";
    id: string | number;
    result?: any;
    error?: {
        code: number;
        message: string;
        data?: any;
    };
};

// =====================
// Tool Executor
// =====================
export async function executeTool(
    tool: any,
    args: Record<string, any> = {},
    baseUrl: string
) {
    const x = tool["x-props"] || {};

    const method = (x.method || "GET").toUpperCase();
    const path = x.path || `/${tool.name}`;
    const url = `${baseUrl}${path}`;

    const opts: RequestInit = {
        method,
        headers: { "Content-Type": "application/json" },
    };

    if (["POST", "PUT", "PATCH", "DELETE"].includes(method)) {
        opts.body = JSON.stringify(args || {});
    }

    const res = await fetch(url, opts);
    const contentType = res.headers.get("content-type") || "";
    const data = contentType.includes("application/json")
        ? await res.json()
        : await res.text();

    return {
        success: res.ok,
        status: res.status,
        method,
        path,
        data,
    };
}

// =====================
// MCP Handler (Async)
// =====================
async function handleMCPRequestAsync(
    request: JSONRPCRequest
): Promise<JSONRPCResponse> {
    const { id, method, params } = request;

    switch (method) {
        case "initialize":
            return {
                jsonrpc: "2.0",
                id,
                result: {
                    protocolVersion: "2024-11-05",
                    capabilities: { tools: {} },
                    serverInfo: { name: "elysia-mcp-server", version: "1.0.0" },
                },
            };

        case "tools/list":
            return {
                jsonrpc: "2.0",
                id,
                result: {
                    tools: tools.map(({ name, description, inputSchema, ["x-props"]: x }) => ({
                        name,
                        description,
                        inputSchema,
                        "x-props": x,
                    })),
                },
            };

        case "tools/call": {
            const toolName = params?.name;
            const tool = tools.find((t) => t.name === toolName);

            if (!tool) {
                return {
                    jsonrpc: "2.0",
                    id,
                    error: { code: -32601, message: `Tool '${toolName}' not found` },
                };
            }

            try {
                const baseUrl =
                    process.env.BUN_PUBLIC_BASE_URL || "http://localhost:3000";
                const result = await executeTool(tool, params?.arguments || {}, baseUrl);
                const data = result.data.data;
                const isObject = typeof data === "object" && data !== null;

                return {
                    jsonrpc: "2.0",
                    id,
                    result: {
                        content: [
                            isObject
                                ? { type: "json", data: data }
                                : { type: "text", text: JSON.stringify(data || result.data || result) },
                        ],
                    },
                };
            } catch (error: any) {
                return {
                    jsonrpc: "2.0",
                    id,
                    error: { code: -32603, message: error.message },
                };
            }
        }

        case "ping":
            return { jsonrpc: "2.0", id, result: {} };

        default:
            return {
                jsonrpc: "2.0",
                id,
                error: { code: -32601, message: `Method '${method}' not found` },
            };
    }
}

// =====================
// Elysia MCP Server
// =====================
export const MCPRoute = new Elysia({
    tags: ["MCP Server"]
})
    .post("/mcp", async ({ request, set }) => {
        if (!tools.length) {
            tools = await getMcpTools(OPENAPI_URL, FILTER_TAG);
        }
        set.headers["Content-Type"] = "application/json";
        set.headers["Access-Control-Allow-Origin"] = "*";

        try {
            const body = await request.json();

            if (!Array.isArray(body)) {
                const res = await handleMCPRequestAsync(body);
                return res;
            }

            const results = await Promise.all(
                body.map((req) => handleMCPRequestAsync(req))
            );
            return results;
        } catch (error: any) {
            set.status = 400;
            return {
                jsonrpc: "2.0",
                id: null,
                error: {
                    code: -32700,
                    message: "Parse error",
                    data: error.message,
                },
            };
        }
    })

    // Tools list (debug)
    .get("/mcp/tools", async ({ set }) => {
        if (!tools.length) {

            tools = await getMcpTools(OPENAPI_URL, FILTER_TAG);
        }
        set.headers["Access-Control-Allow-Origin"] = "*";
        return {
            tools: tools.map(({ name, description, inputSchema, ["x-props"]: x }) => ({
                name,
                description,
                inputSchema,
                "x-props": x,
            })),
        };
    })

    // MCP status
    .get("/mcp/status", ({ set }) => {
        set.headers["Access-Control-Allow-Origin"] = "*";
        return { status: "active", timestamp: Date.now() };
    })

    // Health check
    .get("/health", ({ set }) => {
        set.headers["Access-Control-Allow-Origin"] = "*";
        return { status: "ok", timestamp: Date.now(), tools: tools.length };
    })
    .get("/mcp/init", async ({ set }) => {

        const _tools = await getMcpTools(OPENAPI_URL, FILTER_TAG);
        tools = _tools;
        return {
            success: true,
            message: "MCP initialized",
            tools: tools.length,
        };
    })

    // CORS
    .options("/mcp", ({ set }) => {
        set.headers["Access-Control-Allow-Origin"] = "*";
        set.headers["Access-Control-Allow-Methods"] = "GET,POST,OPTIONS";
        set.headers["Access-Control-Allow-Headers"] =
            "Content-Type,Authorization,X-API-Key";
        set.status = 204;
        return "";
    })
    .options("/mcp/tools", ({ set }) => {
        set.headers["Access-Control-Allow-Origin"] = "*";
        set.headers["Access-Control-Allow-Methods"] = "GET,OPTIONS";
        set.headers["Access-Control-Allow-Headers"] =
            "Content-Type,Authorization,X-API-Key";
        set.status = 204;
        return "";
    });
