#pragma once

#include <nlohmann/json.hpp>
#include <string>
#include <vector>
#include <optional>

namespace celuna::mcp {

using json = nlohmann::json;

/**
 * @brief MCP Tool definition
 */
struct MCPTool {
    std::string name;
    std::string description;
    json inputSchema;

    json toJson() const {
        return {
            {"name", name},
            {"description", description},
            {"inputSchema", inputSchema}
        };
    }

    static MCPTool fromJson(const json& j) {
        MCPTool tool;
        tool.name = j.value("name", "");
        tool.description = j.value("description", "");
        tool.inputSchema = j.value("inputSchema", json::object());
        return tool;
    }
};

/**
 * @brief MCP Resource definition
 */
struct MCPResource {
    std::string uri;
    std::string name;
    std::string description;
    std::string mimeType;

    static MCPResource fromJson(const json& j) {
        MCPResource res;
        res.uri = j.value("uri", "");
        res.name = j.value("name", "");
        res.description = j.value("description", "");
        res.mimeType = j.value("mimeType", "");
        return res;
    }
};

/**
 * @brief Result of a tool call
 */
struct MCPToolResult {
    std::vector<json> content;  // Array of content blocks
    bool isError = false;

    json toJson() const {
        return {
            {"content", content},
            {"isError", isError}
        };
    }
};

/**
 * @brief MCP Server capabilities
 */
struct MCPCapabilities {
    bool hasTools = false;
    bool hasResources = false;
    bool hasPrompts = false;

    static MCPCapabilities fromJson(const json& j) {
        MCPCapabilities caps;
        if (j.contains("tools")) caps.hasTools = true;
        if (j.contains("resources")) caps.hasResources = true;
        if (j.contains("prompts")) caps.hasPrompts = true;
        return caps;
    }
};

/**
 * @brief MCP Server info after initialization
 */
struct MCPServerInfo {
    std::string name;
    std::string version;
    MCPCapabilities capabilities;

    static MCPServerInfo fromJson(const json& j) {
        MCPServerInfo info;
        info.name = j.value("name", "unknown");
        info.version = j.value("version", "0.0.0");
        if (j.contains("capabilities")) {
            info.capabilities = MCPCapabilities::fromJson(j["capabilities"]);
        }
        return info;
    }
};

/**
 * @brief JSON-RPC request
 */
struct JsonRpcRequest {
    std::string jsonrpc = "2.0";
    int id;
    std::string method;
    json params;

    json toJson() const {
        json j = {
            {"jsonrpc", jsonrpc},
            {"id", id},
            {"method", method}
        };
        if (!params.is_null()) {
            j["params"] = params;
        }
        return j;
    }
};

/**
 * @brief JSON-RPC response
 */
struct JsonRpcResponse {
    std::string jsonrpc;
    int id;
    std::optional<json> result;
    std::optional<json> error;

    bool isError() const { return error.has_value(); }

    static JsonRpcResponse fromJson(const json& j) {
        JsonRpcResponse resp;
        resp.jsonrpc = j.value("jsonrpc", "2.0");
        resp.id = j.value("id", 0);
        if (j.contains("result")) {
            resp.result = j["result"];
        }
        if (j.contains("error")) {
            resp.error = j["error"];
        }
        return resp;
    }
};

/**
 * @brief MCP Server configuration
 */
struct MCPServerConfig {
    std::string name;
    std::string command;
    std::vector<std::string> args;
    std::map<std::string, std::string> env;
    bool enabled = true;

    static MCPServerConfig fromJson(const std::string& name, const json& j) {
        MCPServerConfig config;
        config.name = name;
        config.command = j.value("command", "");
        config.enabled = j.value("enabled", true);

        if (j.contains("args") && j["args"].is_array()) {
            for (const auto& arg : j["args"]) {
                config.args.push_back(arg.get<std::string>());
            }
        }

        if (j.contains("env") && j["env"].is_object()) {
            for (auto& [key, value] : j["env"].items()) {
                config.env[key] = value.get<std::string>();
            }
        }

        return config;
    }
};

} // namespace celuna::mcp
