#include "module.h"

#include <balancer/kernel/http/parser/response_builder.h>
#include <balancer/kernel/http/parser/http.h>
#include <balancer/kernel/module/module.h>
#include <balancer/kernel/rpslimiter/quota_manager.h>

using namespace NSrvKernel;
using namespace NSrvKernel::NRpsLimiter;

Y_TLS(quota_sync) {
    TResponse Response;
};

MODULE_WITH_TLS(quota_sync) {
public:
    TModule(const TModuleParams& mp)
        : TModuleBase(mp)
        , QuotaManager_(mp.Control->GetQuotaManager())
    {
        Y_ENSURE_EX(mp.Control->GetQuotaManager(),
            TConfigParseError() << "rpslimiter_instance required");

        Config->ForEach(this);
    }

private:
    THolder<TTls> DoInitTls(IWorkerCtl*) override {
        auto tls = MakeHolder<TTls>();
        tls->Response = BuildResponse().Version11().Code(HTTP_OK);
        return tls;
    }

    TError DoRun(const TConnDescr& descr, TTls& tls) const noexcept override {
        TChunkList requestBody;
        if (TError error = RecvAll(descr.Input, requestBody, TInstant::Max())) {
            descr.ExtraAccessLog.SetSummary(GetHandle()->Name(), "client read error");
            return error;
        }
        TPeerQuotas hq;
        if (TError error = ParsePeerQuotas(Union(requestBody)->AsStringBuf()).AssignTo(hq)) {
            descr.ExtraAccessLog.SetSummary(GetHandle()->Name(), "quotas parse error");
            return error;
        }
        // TODO(velavokr): do not ignore
        Y_UNUSED(QuotaManager_->Storage.UpdatePeerQuotas(hq, descr.Properties->Start));

        TString responseState = RenderPeerQuotas(QuotaManager_->Storage.GenPeerQuotasFromLocal(descr.Properties->Start));

        TResponse response = tls.Response;
        response.Props().ContentLength = responseState.size();

        Y_TRY(TError, error) {
            Y_PROPAGATE_ERROR(descr.Output->SendHead(std::move(response), false, TInstant::Max()));
            Y_PROPAGATE_ERROR(descr.Output->Send(TChunkList{responseState}, TInstant::Max()));
            return descr.Output->SendEof(TInstant::Max());
        } Y_CATCH {
            descr.ExtraAccessLog.SetSummary(GetHandle()->Name(), "client write error");
            return error;
        };

        descr.ExtraAccessLog.SetSummary(GetHandle()->Name(), "success");
        return {};
    }

private:
    TQuotaManager* QuotaManager_;
};

IModuleHandle* NModQuotaSync::Handle() {
    return TModule::Handle();
}
