#include "module.h"

#include <balancer/kernel/http/parser/common_headers.h>
#include <balancer/kernel/http/parser/httpencoder.h>
#include <balancer/kernel/http/parser/http.h>
#include <balancer/kernel/memory/chunks.h>
#include <balancer/kernel/module/module.h>
#include <balancer/kernel/regexp/regexp_pire.h>
#include <balancer/kernel/custom_io/stream.h>

#include <util/generic/string.h>

using namespace NSrvKernel;
using namespace NModH100;

namespace {
class T100ContinueResponseHolder {
public:
    T100ContinueResponseHolder() {
        TString statusLine(TString("HTTP/1.1 ").append(HttpCodeStrEx(100)).append("\r\n\r\n"));
        TryRethrowError(Response_.Parse(std::move(statusLine)));
        Response_.Props().ContentLength.reset();
    }

    const TResponse& Response() const noexcept {
        return Response_;
    }
private:
    TResponse Response_;
};
}

MODULE_BASE(h100, TModuleWithSubModule) {
public:
    TModule(const TModuleParams& mp)
        : TModuleBase(mp)
    {
        Config->ForEach(this);

        if (!Submodule_) {
            ythrow TConfigParseError() << "no module configured";
        }

        Y_UNUSED(TExpectFsm::Instance());
        Y_UNUSED(T100ContinueFsm::Instance());
    }

private:
    START_PARSE {
        Submodule_.Reset(Loader->MustLoad(key, Copy(value->AsSubConfig())).Release());
        return;
    } END_PARSE

    TError DoRun(const TConnDescr& descr) const noexcept override {
        if (Y_LIKELY(descr.Request)) {
            bool has100cont = false;
            EraseNodesIf(descr.Request->Headers(), [&](auto& header) {
                if (Match(TExpectFsm::Instance(), header.first.AsStringBuf())) {
                    if (!has100cont) {
                        for (const auto& headerValue : header.second) {
                            if (Match(T100ContinueFsm::Instance(), headerValue.AsStringBuf())) {
                                has100cont = true;
                            }
                        }
                    }
                    return true;
                } else {
                    return false;
                }
            });

            if (has100cont) {
                TResponse response = ContinueResponse_.Response();
                Y_TRY(TError, error) {
                    Y_PROPAGATE_ERROR(descr.Output->SendHead(std::move(response), false, TInstant::Max()));
                    return descr.Output->SendEof(TInstant::Max());
                } Y_CATCH {
                    descr.ExtraAccessLog.SetSummary(GetHandle()->Name(), "client write error");
                    return error;
                };
                descr.ExtraAccessLog << " 100-continue";
            }
        }

        return Submodule_->Run(descr);
    }

    bool DoExtraAccessLog() const noexcept override {
        return true;
    }

private:
    T100ContinueResponseHolder ContinueResponse_;
};

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