BareGit
#pragma once

#include <mw/url.hpp>

#include "message.hpp"
#include "tool.hpp"

namespace agent_smith::mcp
{

/// An unsolicited server notification.
struct Notification
{
    /// JSON-RPC notification method.
    std::string method;
    /// Original notification parameters.
    nlohmann::json params;
};
/// Observable connection transitions.
enum class LifecycleEventKind
{
    /// Initialization and discovery succeeded.
    CONNECTED,
    /// Transport failed or explicit shutdown began.
    DISCONNECTED,
    /// A replacement connection is being initialized.
    RECONNECTING,
    /// Replacement connection could not be initialized.
    RECONNECT_FAILED
};
/// A connection transition for one configured server.
struct LifecycleEvent
{
    /// Transition being reported.
    LifecycleEventKind kind;
    /// Configured server alias.
    std::string server_alias;
    /// Human-readable transition context.
    std::string message;
};
/// Server notification or connection transition.
using Event = std::variant<Notification, LifecycleEvent>;
/// Best-effort observer; must not block or reenter the client.
using EventHook = std::function<void(const Event&)>;
/// JSON-RPC request envelope.
struct Request
{
    /// Client-generated request identifier.
    std::int64_t id;
    /// Method being invoked.
    std::string method;
    /// Method parameters.
    nlohmann::json params;
};
/// Remote JSON-RPC failure.
struct RpcError
{
    /// Server's JSON-RPC error code.
    int code;
    /// Server's error description.
    std::string message;
    /// Optional original error details.
    nlohmann::json data;
};
/// JSON-RPC response envelope.
struct Response
{
    /// Identifier of the request being answered.
    std::int64_t id;
    /// Result payload or remote error.
    std::variant<nlohmann::json, RpcError> body;
};
/// Server identity and original metadata.
struct ServerInfo
{
    /// Reported implementation name.
    std::string name;
    /// Reported implementation version.
    std::string version;
    /// Original serverInfo object.
    nlohmann::json raw;
};
/// One advertised resource.
struct ResourceInfo
{
    /// Identifier passed to resources/read.
    std::string uri;
    /// Human-readable resource name.
    std::string name;
    /// Optional server description.
    std::optional<std::string> description;
    /// Optional advertised media type.
    std::optional<std::string> mime_type;
    /// Original resource descriptor.
    nlohmann::json raw;
};
/// One advertised prompt template.
struct PromptInfo
{
    /// Name passed to prompts/get.
    std::string name;
    /// Optional server description.
    std::optional<std::string> description;
    /// Original argument descriptors.
    nlohmann::json arguments = nlohmann::json::array();
    /// Original prompt descriptor.
    nlohmann::json raw;
};
/// Text-only rendered prompt and original response.
struct PromptRendering
{
    /// Rendered user and assistant text messages.
    std::vector<Message> messages;
    /// Original prompts/get response.
    nlohmann::json raw;
};
/// Supported transport selectors; HTTP arrives in milestone 15.
enum class McpTransportKind
{
    /// A child process using newline-delimited JSON on stdin/stdout.
    STDIO,
    /// Reserved for the future HTTP transport.
    STREAMABLE_HTTP
};
/// Executable and arguments, without shell interpretation.
struct StdioServerConfig
{
    /// Executable followed by arguments; passed directly without a shell.
    std::vector<std::string> argv;
};
/// Streamable HTTP endpoint configuration.
struct HttpServerConfig
{
    /// Future HTTP endpoint; not used by stdio.
    mw::URL endpoint;
};
/// Connection, discovery, filtering and timeout policy.
struct ServerConfig
{
    /// Unique alias used in local tool names; letters, digits, '_' and '-'.
    std::string alias;
    /// Transport selector; must match transport's active variant.
    McpTransportKind kind = McpTransportKind::STDIO;
    /// Transport-specific configuration.
    std::variant<StdioServerConfig, HttpServerConfig> transport;
    /// Reserved HTTP authentication; not used by stdio.
    std::optional<std::string> bearer_token;
    /// Reserved HTTP headers; not used by stdio.
    std::vector<std::pair<std::string, std::string>> extra_headers;
    /// Default budget for initialization, discovery, resources and prompts.
    std::chrono::milliseconds request_timeout{30000};
    /// Allowed remote tool names; empty permits all non-denied names.
    std::vector<std::string> allow_tools;
    /// Excluded remote names; takes precedence over allow_tools.
    std::vector<std::string> deny_tools;
    /// Fail connection when tools/list is unavailable.
    bool tools_required = true;
    /// Reconnect once for read-only failure or a later operation after loss.
    bool reconnect_on_transport_error = true;
};
/// Bidirectional framed transport. Runtime must outlive the transport.
class Transport
{
public:
    /// Release transport resources.
    virtual ~Transport() = default;
    /// Send one frame within an absolute deadline. A failed write may have
    /// delivered a prefix and must not be automatically replayed.
    virtual mw::Task<mw::E<void>>
    send(nlohmann::json frame,
         std::chrono::steady_clock::time_point deadline =
             std::chrono::steady_clock::now() + std::chrono::milliseconds(100),
         CancellationToken cancel = {}) = 0;
    /// Receive one frame or a typed failure.
    virtual mw::Task<mw::E<nlohmann::json>> recv() = 0;
    /// Wake an idle receiver without closing descriptors during active I/O.
    virtual void interrupt() noexcept = 0;
    /// Stop I/O and reap the child.
    virtual mw::Task<mw::E<void>> close() = 0;
};
/// Concurrent MCP requests over one stdio connection.
/// Runtime must outlive this client and all in-flight calls.
/// Discovery getters must not be used while a reconnect is in progress.
class Client
{
public:
    /// Initialize and discover a server before returning it.
    static mw::Task<mw::E<std::unique_ptr<Client>>>
    connect(Runtime& runtime, ServerConfig config, EventHook hook = {});
    /// Close owned transport, including when shutdown was omitted.
    ~Client();
    /// Negotiated server metadata.
    const ServerInfo& info() const;
    /// Filtered tool schemas, retaining original remote names.
    const std::vector<ToolSchema>& tools() const;
    /// Advertised resources.
    const std::vector<ResourceInfo>& resources() const;
    /// Advertised prompt templates.
    const std::vector<PromptInfo>& prompts() const;
    /// Stable configured server alias.
    const std::string& alias() const;
    /// Invoke a tool; never automatically replay a written request.
    mw::Task<mw::E<ToolResult>> callTool(std::string name, nlohmann::json args,
                                         std::chrono::milliseconds timeout,
                                         CancellationToken cancel = {});
    /// Read text resources; binary contents are unsupported.
    mw::Task<mw::E<std::string>> readResource(
        std::string uri, CancellationToken cancel = {},
        std::optional<std::chrono::milliseconds> timeout = std::nullopt);
    /// Render a text-only prompt without applying it to an agent.
    mw::Task<mw::E<PromptRendering>> getPrompt(std::string name,
                                               nlohmann::json args);
    /// Replace the notification/lifecycle observer.
    void setEventHook(EventHook hook);
    /// Close connection and wake pending operations.
    mw::Task<mw::E<void>> shutdown();

private:
    struct State;
    explicit Client(std::shared_ptr<State> state);
    static mw::Task<mw::E<void>> receive(std::shared_ptr<State> state);
    mw::Task<mw::E<nlohmann::json>> request(std::string method,
                                            nlohmann::json params,
                                            std::chrono::milliseconds timeout,
                                            CancellationToken cancel = {},
                                            bool allow_reconnect = true);
    mw::Task<mw::E<void>> discover();
    mw::Task<mw::E<void>> reconnect(std::shared_ptr<State> previous);
    std::mutex state_mutex_;
    bool reconnecting_ = false;
    bool stopped_ = false;
    std::string alias_;
    std::chrono::milliseconds default_timeout_;
    std::shared_ptr<State> state_;
    ServerInfo info_;
    std::vector<ToolSchema> tools_;
    std::vector<ResourceInfo> resources_;
    std::vector<PromptInfo> prompts_;
};

/// Adapt all remote tools and a resource reader; retain the client safely.
std::vector<std::unique_ptr<Tool>> adaptTools(std::shared_ptr<Client> client);

} // namespace agent_smith::mcp