#ifndef ADAPTERS_SMO_CPS_CALLBACK_AREQ_H
#define ADAPTERS_SMO_CPS_CALLBACK_AREQ_H


#include <atomic>
#include <coroutine>
#include <functional>
#include <memory>

#include <boost/asio/io_context.hpp>
#include <boost/asio/post.hpp>
#include <spinscale/cps/callback.h>

namespace smo {
namespace cpsBoundary {

/** Eager-start CPS callback -> coroutine adapter (mirrors
 *  PuppetThread::ViralThreadLifetimeMgmtInvoker).
 */
template <typename Result, typename CallbackFn, typename StartFn>
class CpsCallbackAReq
{
public:
	struct AsyncState
	{
		std::atomic<bool> settled{false};
		Result result{};
		std::coroutine_handle<> callerSchedHandle;
	};

	CpsCallbackAReq(
		boost::asio::io_context &resumeIoContext,
		StartFn startFn)
	: asyncState(std::make_shared<AsyncState>()),
	resumeIoContext(resumeIoContext)
	{
		startFn(sscl::cps::Callback<CallbackFn>{
			nullptr,
			[this](auto &&...args)
			{
				storeResult(std::forward<decltype(args)>(args)...);
				signalSettledAndResumeCaller();
			}});
	}

	bool await_ready() const noexcept
	{
		return asyncState->settled.load(std::memory_order_acquire);
	}

	bool await_suspend(std::coroutine_handle<> callerSchedHandle) noexcept
	{
		if (asyncState->settled.load(std::memory_order_acquire)) {
			return false;
		}

		asyncState->callerSchedHandle = callerSchedHandle;
		return true;
	}

	Result await_resume() noexcept
	{
		return asyncState->result;
	}

private:
	template <typename... Args>
	void storeResult(Args &&...args)
	{
		asyncState->result = Result{std::forward<Args>(args)...};
	}

	void signalSettledAndResumeCaller() noexcept
	{
		asyncState->settled.store(true, std::memory_order_release);

		std::coroutine_handle<> handle = asyncState->callerSchedHandle;
		if (!handle) {
			return;
		}

		boost::asio::post(resumeIoContext, handle);
	}

	std::shared_ptr<AsyncState> asyncState;
	boost::asio::io_context &resumeIoContext;
};

} // namespace cpsBoundary
} // namespace smo

#endif // ADAPTERS_SMO_CPS_CALLBACK_AREQ_H
