#include "module.h"
#include "hasher.h"
#include "subnet.h"

#include <balancer/kernel/custom_io/stream.h>
#include <balancer/kernel/helpers/default_instance.h>
#include <balancer/kernel/module/iface.h>
#include <balancer/kernel/module/module.h>

#include <util/generic/strbuf.h>

using namespace NConfig;
using namespace NSrvKernel;
using namespace NModHasher;

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

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

        if (!Hasher_) {
            ythrow TConfigParseError() << "no hasher configured";
        }
    }

    START_PARSE {
        struct TImplOptions: public TFsm , public TWithDefaultInstance<TImplOptions> {
            TImplOptions()
                : TFsm("take_ip_from|subnet_v4_mask|subnet_v6_mask")
            {}
        };

        if (TMatcher(TImplOptions::Instance()).Match(key).Final()) {
            return;
        }

        ON_KEY("mode", Mode_) {
            Hasher_.Reset(CommonHashers()->Load(value->AsString(), *this));
            return;
        }

        ON_KEY("combine_hashes", CombineHashes_) {
            return;
        }

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

    } END_PARSE

    TError DoRun(const TConnDescr& descr) const noexcept override {
        TRequestHash hs;
        if (TError error = Hasher_->CalcHash(descr).AssignTo(hs)) {
            descr.ExtraAccessLog.SetSummary(GetHandle()->Name(), "hasher error");
            return error;
        }
        if (CombineHashes_ && descr.Hash) {
            hs = CombineHashes(hs, descr.Hash);
        }
        return Submodule_->Run(descr.Copy(hs));
    }

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

    void DoCheckConstraints() const override {
        if (Mode_ != "subnet") {
            CheckHasParent("http");
        }
    }

private:
    THolder<IHasher> Hasher_;
    TString Mode_;
    bool CombineHashes_ = false;
};

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