BareGit
#pragma once

#include <chrono>
#include <cstddef>
#include <memory>
#include <mutex>
#include <stop_token>
#include <vector>
#include <type_traits>

#include <mw/http_client_async.hpp>

#include "detail/blocking_pool.hpp"
#include "detail/started_task.hpp"

namespace agent_smith::internal
{
class TimerService;
struct BackgroundTasks;
}

namespace agent_smith
{

/// Configuration shared by Agent Smith's runtime services.
struct RuntimeConfig
{
    /// Number of workers reserved for future non-HTTP blocking work.
    std::size_t blocking_pool_size = 4;

    /// Maximum time allowed to establish an HTTP connection.
    std::chrono::seconds connection_timeout{60};

    /// Maximum time allowed for an HTTP transfer.
    std::chrono::seconds transfer_timeout{60};
};

/// Owns the shared asynchronous HTTP session used by Agent Smith clients.
class Runtime
{
public:
    /// Construct a runtime using the supplied shared-service configuration.
    explicit Runtime(RuntimeConfig cfg = {});

    /// Drain retained tool work, then release runtime resources.
    /// A non-cooperative timed-out tool can delay shutdown until it finishes.
    ~Runtime();

    Runtime(const Runtime&) = delete;
    Runtime& operator=(const Runtime&) = delete;
    Runtime(Runtime&&) = delete;
    Runtime& operator=(Runtime&&) = delete;

    /// Return the shared asynchronous HTTP session.
    mw::HTTPSessionAsync& http();

    /// Run non-HTTP work on the internal worker pool.
    template <typename F>
    auto runOnWorker(F&& function)
        -> mw::Task<internal::ExpectedResult<std::invoke_result_t<F&>>>;

    /// Suspend without blocking the caller's coroutine thread.
    mw::Task<mw::E<void>> sleepFor(std::chrono::milliseconds duration);

    /// Suspend until the duration elapses or stop is requested.
    /// An early wake is successful; inspect the token to distinguish it.
    mw::Task<mw::E<void>> sleepFor(std::chrono::milliseconds duration,
                                 std::stop_token stop);

private:
    friend struct internal::BackgroundTasks;
    void retain(internal::StartedTask<void> task);

    RuntimeConfig cfg_;
    std::unique_ptr<mw::HTTPSessionAsync> http_;
    std::unique_ptr<internal::BlockingPool> blocking_pool_;
    std::unique_ptr<internal::TimerService> timer_service_;
    std::mutex background_mutex_;
    std::vector<internal::StartedTask<void>> background_tasks_;
};

template <typename F>
auto Runtime::runOnWorker(F&& function)
    -> mw::Task<internal::ExpectedResult<std::invoke_result_t<F&>>>
{
    co_return co_await blocking_pool_->run(std::forward<F>(function));
}

} // namespace agent_smith