#pragma once
#include <drogon/DrObject.h>
#include <drogon/drogon_callbacks.h>
#include <drogon/HttpRequest.h>
#include <drogon/HttpResponse.h>
#include <drogon/HttpMiddleware.h>
#include <memory>
#ifdef __cpp_impl_coroutine
#include <drogon/utils/coroutine.h>
#endif
namespace drogon
{
class DROGON_EXPORT HttpFilterBase : public virtual DrObjectBase,
                                     public HttpMiddlewareBase
{
  public:
    virtual void doFilter(const HttpRequestPtr &req,
                          FilterCallback &&fcb,
                          FilterChainCallback &&fccb) = 0;
    ~HttpFilterBase() override = default;
  private:
    void invoke(const HttpRequestPtr &req,
                MiddlewareNextCallback &&nextCb,
                MiddlewareCallback &&mcb) final
    {
        auto mcbPtr = std::make_shared<MiddlewareCallback>(std::move(mcb));
        doFilter(
            req,
            [mcbPtr](const HttpResponsePtr &resp) {
                (*mcbPtr)(resp);
            },  
            [nextCb = std::move(nextCb), mcbPtr]() mutable {
                nextCb([mcbPtr = std::move(mcbPtr)](
                           const HttpResponsePtr &resp) { (*mcbPtr)(resp); });
            }  
        );
    }
};
template <typename T, bool AutoCreation = true>
class HttpFilter : public DrObject<T>, public HttpFilterBase
{
  public:
    static constexpr bool isAutoCreation{AutoCreation};
    ~HttpFilter() override = default;
};
#ifdef __cpp_impl_coroutine
template <typename T, bool AutoCreation = true>
class HttpCoroFilter : public DrObject<T>, public HttpFilterBase
{
  public:
    static constexpr bool isAutoCreation{AutoCreation};
    ~HttpCoroFilter() override = default;
    void doFilter(const HttpRequestPtr &req,
                  FilterCallback &&fcb,
                  FilterChainCallback &&fccb) final
    {
        drogon::async_run([this,
                           req,
                           fcb = std::move(fcb),
                           fccb = std::move(fccb)]() mutable -> drogon::Task<> {
            HttpResponsePtr resp;
            try
            {
                resp = co_await doFilter(req);
            }
            catch (const std::exception &ex)
            {
                internal::handleException(ex, req, std::move(fcb));
                co_return;
            }
            catch (...)
            {
                LOG_ERROR << "Exception not derived from std::exception";
                co_return;
            }
            if (resp)
            {
                fcb(resp);
            }
            else
            {
                fccb();
            }
        });
    }
    virtual Task<HttpResponsePtr> doFilter(const HttpRequestPtr &req) = 0;
};
#endif
}  