BareGit
#include "detail/transport_stdio.hpp"

#include <mw/exec.hpp>

namespace agent_smith::mcp::internal
{
namespace
{
mw::Error failure(std::string message)
{
    return McpError{std::move(message), {}, std::nullopt, nullptr, true};
}

class StdioTransport final : public Transport
{
public:
    StdioTransport(Runtime& runtime, mw::Pipe input, mw::Pipe output,
                   mw::Process process)
        : runtime_(runtime), input_(std::move(input)),
          output_(std::move(output)), process_(std::move(process))
    {
    }

    mw::Task<mw::E<void>> send(nlohmann::json frame,
                               std::chrono::steady_clock::time_point deadline,
                               CancellationToken cancel) override
    {
        std::string bytes = frame.dump() + "\n";
        std::function<mw::E<void>()> work =
            [this, bytes = std::move(bytes), deadline, cancel]
        {
            std::unique_lock lock(write_mutex_, std::defer_lock);
            while(!lock.try_lock_for(std::chrono::milliseconds(2)))
            {
                if(cancel.cancelled() || interrupted_ ||
                   std::chrono::steady_clock::now() >= deadline)
                {
                    return mw::E<void>(std::unexpected(failure(
                        "stdio write stopped before lock acquisition")));
                }
            }
            if(closed_)
            {
                return mw::E<void>(std::unexpected(failure("stdio closed")));
            }
            std::size_t offset = 0;
            while(offset < bytes.size())
            {
                const auto now = std::chrono::steady_clock::now();
                if(cancel.cancelled() || interrupted_ || now >= deadline)
                {
                    return mw::E<void>(std::unexpected(failure(
                        "stdio write stopped (request may be partial)")));
                }
                auto result = input_.writeUntil(
                    std::string_view(bytes).substr(offset),
                    std::min(deadline, now + std::chrono::milliseconds(2)));
                if(!result)
                {
                    return mw::E<void>(std::unexpected(failure(
                        "stdio write failed (request may be partial)")));
                }
                offset += result->bytes_written;
            }
            return mw::E<void>{};
        };
        co_return co_await runtime_.runOnWorker(std::move(work));
    }

    mw::Task<mw::E<nlohmann::json>> recv() override
    {
        while(true)
        {
            if(interrupted_)
            {
                co_return std::unexpected(failure("stdio interrupted"));
            }
            const auto newline = buffer_.find('\n');
            if(newline != std::string::npos)
            {
                auto frame = nlohmann::json::parse(buffer_.substr(0, newline),
                                                   nullptr, false);
                buffer_.erase(0, newline + 1);
                if(frame.is_discarded())
                {
                    co_return std::unexpected(
                        McpError{"invalid stdio JSON frame"});
                }
                co_return frame;
            }
            std::function<mw::E<mw::PipeReadResult>()> work = [this]
            {
                std::lock_guard lock(read_mutex_);
                if(closed_)
                {
                    return mw::E<mw::PipeReadResult>(
                        std::unexpected(failure("stdio closed")));
                }
                return output_.tryRead(8192);
            };
            auto bytes = co_await runtime_.runOnWorker(std::move(work));
            if(!bytes)
            {
                co_return std::unexpected(failure(bytes.error().msg()));
            }
            if(bytes->state == mw::PipeState::EOF_REACHED)
            {
                co_return std::unexpected(failure("MCP server closed stdout"));
            }
            buffer_ += bytes->data;
            if(buffer_.size() > 4 * 1024 * 1024)
            {
                co_return std::unexpected(McpError{"MCP frame too large"});
            }
            if(bytes->state == mw::PipeState::WOULD_BLOCK)
            {
                co_await runtime_.sleepFor(std::chrono::milliseconds(2));
            }
        }
    }

    mw::Task<mw::E<void>> close() override
    {
        interrupt();
        std::function<mw::E<void>()> work = [this]
        {
            std::scoped_lock lock(read_mutex_, write_mutex_);
            if(closed_)
            {
                return mw::E<void>{};
            }
            closed_ = true;
            std::ignore = input_.closeInput();
            std::ignore = output_.closeOutput();
            auto exited = process_.waitFor(std::chrono::milliseconds(50));
            if(!exited || exited->state != mw::ProcessWaitState::COMPLETE)
            {
                exited = process_.shutdown(std::chrono::milliseconds(50),
                                           std::chrono::steady_clock::now() +
                                               std::chrono::milliseconds(500));
            }
            if(!exited)
            {
                return mw::E<void>(
                    std::unexpected(failure(exited.error().msg())));
            }
            if(exited->state != mw::ProcessWaitState::COMPLETE)
            {
                return mw::E<void>(std::unexpected(
                    failure("MCP child shutdown deadline expired")));
            }
            return mw::E<void>{};
        };
        co_return co_await runtime_.runOnWorker(std::move(work));
    }

    void interrupt() noexcept override
    {
        interrupted_ = true;
    }

private:
    Runtime& runtime_;
    mw::Pipe input_;
    mw::Pipe output_;
    mw::Process process_;
    std::mutex read_mutex_;
    std::timed_mutex write_mutex_;
    bool closed_ = false;
    std::atomic<bool> interrupted_{false};
    std::string buffer_;
};
} // namespace

mw::E<std::unique_ptr<Transport>> openStdio(Runtime& runtime,
                                            const StdioServerConfig& config)
{
    auto input = mw::Pipe::create();
    auto output = mw::Pipe::create();
    if(!input)
    {
        return std::unexpected(failure(input.error().msg()));
    }
    if(!output)
    {
        return std::unexpected(failure(output.error().msg()));
    }
    std::vector<std::string_view> argv;
    for(const auto& arg : config.argv)
    {
        argv.push_back(arg);
    }
    auto process = mw::Process::execArgs(&*input, argv, &*output);
    if(!process)
    {
        return std::unexpected(failure(process.error().msg()));
    }
    return std::unique_ptr<Transport>(new StdioTransport(
        runtime, std::move(*input), std::move(*output), std::move(*process)));
}
} // namespace agent_smith::mcp::internal