#include <balancer/serval/core/config.h>

#include <library/cpp/json/writer/json.h>

static NSv::TAction RemoteLog(const YAML::Node& args, NSv::TAuxData& aux) {
    struct TRemoteLogStream : NSv::TStreamProxy {
    public:
        TRemoteLogStream(NSv::IStreamPtr s, const NSv::TAction& target, TString data, cone::mguard& bg)
            : NSv::TStreamProxy(std::move(s))
            , Target_(target)
            , Data_(std::move(data))
            , Bg_(bg)
        {
        }

        ~TRemoteLogStream() {
            Bg_.add([proxy = Target_, data = TString::Join(ToString(Data_.size()), " ", Data_)] {
                NSv::IStreamPtr fake = NSv::ConstRequestStream(
                    {"POST", "/", {{":authority", "unknown"}, {":scheme", "http"}, {"content-type", "application/json"}}}, data,
                    [](NSv::THead&) {
                        return true;
                    },
                    [](TStringBuf) {
                        return true;
                    },
                    [](NSv::THeaderVector&) {
                        return true;
                    }
                );
                return proxy(fake) || mun_errno != ECANCELED;
            });
        }

        bool WriteHead(NSv::THead& head) noexcept override {
            if (!head.IsInformational()) {
                Data_.insert(Data_.size() - 1, ",\"status\":" + ToString(head.Code));
            }
            return NSv::TStreamProxy::WriteHead(head);
        }

    private:
        const NSv::TAction& Target_;
        TString Data_;
        cone::mguard& Bg_;
    };

    CHECK_NODE(args, args.IsMap(), "an argument is required");
    auto proxy = aux.Action(args.begin()->second);
    auto uaas = NSv::Optional(args["uaas"], false);
    return [=, bg = MakeHolder<NSv::TThreadLocal<cone::mguard>>()](NSv::IStreamPtr& sc) {
        auto rqh = sc->Head();
        if (!rqh) {
            return false;
        }
        NJsonWriter::TBuf out(NJsonWriter::HEM_UNSAFE);
        out.BeginObject();
        out.WriteKey("method").WriteString(rqh->Method);
        out.WriteKey("request").WriteString(rqh->PathWithQuery);
        out.WriteKey("protocol").WriteString("HTTP/1.1");
        out.WriteKey("addr").WriteString(sc->Peer().FormatFull());
        out.WriteKey("headers").BeginList();
        for (const auto& h : *rqh) {
            out.BeginList().WriteString(h.first).WriteString(h.second).EndList();
        }
        out.EndList();
        out.WriteKey("timestamp").WriteLongLong(TInstant::Now().Seconds()); // FIXME should be start time
        if (uaas) {
            out.WriteKey("uaas_mode").WriteString("true");
        }
        out.EndObject();
        sc = std::make_shared<TRemoteLogStream>(std::move(sc), proxy, out.Str(), bg->GetOrCreate());
        return true;
    };
}

SV_DEFINE_ACTION("remote-log", RemoteLog);
