#include "module.h"

#include <balancer/kernel/custom_io/stream.h>
#include <balancer/kernel/helpers/errors.h>
#include <balancer/kernel/log/errorlog.h>
#include <balancer/kernel/log/logbackend.h>
#include <balancer/kernel/module/module.h>
#include <balancer/kernel/net/address.h>
#include <balancer/kernel/net/format.h>

using namespace NConfig;
using namespace NSrvKernel;
using namespace NModErrorLog;

Y_TLS(errorlog) {
    THolder<TLog> Log;
};

MODULE_WITH_TLS_BASE(errorlog, TModuleWithSubModule) {
    TModule(const TModuleParams& mp)
        : TModuleBase(mp)
    {
        struct TParser: public IConfig::IFunc {
            TParser(TModule* parent) noexcept
                : Parent(parent)
                , Priority(TLOG_ERR)
            {}

            START_PARSE {
                ON_KEY("log", Name) {
                    return;
                }

                TString level;
                ON_KEY("log_level", level) {
                    if (level == "CRITICAL") {
                        Priority = TLOG_CRIT;
                    } else if (level == "ERROR") {
                        Priority = TLOG_ERR;
                    } else if (level == "INFO") {
                        Priority = TLOG_INFO;
                    } else if (level == "DEBUG") {
                        Priority = TLOG_DEBUG;
                    } else {
                        ythrow TConfigParseError() << "unknown log_level " << level;
                    }

                    return;
                }

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

                    return;
                }
            } END_PARSE

            TModule* const Parent{ nullptr };
            TString Name;
            ELogPriority Priority;
        } parser(this);

        Config->ForEach(&parser);

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

        // no need to wrap meta log backend in priority log backend since we do check priority in
        // logging macro. It may break if log would be passed as pointer and kernel's errorlog
        // macro won't be used
        Control->CreateLog(parser.Name);
        LogName_ = std::move(parser.Name);
        LogPriority_ = parser.Priority;
    }

private:
    THolder<TTls> DoInitTls(IWorkerCtl* process) override {
        auto tls = MakeHolder<TTls>();
        tls->Log.Reset(MakeHolder<TLog>(MakeHolder<TMetaLogBackend>(process->GetLog(LogName_))));
        tls->Log->SetDefaultPriority(LogPriority_);
        return tls;
    }

    TError DoRun(const TConnDescr& descr, TTls& tls) const noexcept override {
        auto newDescr = descr.Copy();
        newDescr.ErrorLog = tls.Log.Get();
        Y_TRY(TError, error) {
            return Submodule_->Run(newDescr);
        } Y_CATCH {
            Y_HTTPD_LOG_IMPL(newDescr.ErrorLog, TLOG_ERR, NAME, descr, GetErrorMessage(error));
            return error;
        }
        return {};
    }

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

private:
    TString LogName_;
    ELogPriority LogPriority_;
};

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