#include "module.h"

#include <library/cpp/tvmauth/client/facade.h>

#include <balancer/kernel/http/parser/http.h>
#include <balancer/kernel/module/module.h>
#include <balancer/kernel/tvm/tvm.h>

using namespace NSrvKernel;

MODULE(tvm_service_router) {
private:
    class TRouteParser : public TModuleParams, public IConfig::IFunc {
    public:
        TRouteParser(
            const TString& name,
            THashMap<NTvmAuth::TTvmId, THolder<IModule>>& routes,
            THolder<IModule>& defaultRoute,
            const TModuleParams& mp
        )
            : TModuleParams(mp)
            , Name_(name)
        {
            Config->ForEach(this);

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

            if (!IsDefault() && Src_ == 0) {
                ythrow TConfigParseError() << "no prefix specified";
            }

            if (IsDefault()) {
                defaultRoute = std::move(Slave_);
            } else {
                routes[Src_] = std::move(Slave_);
            }
        }

        bool IsDefault() const noexcept {
            return Name_ == "default";
        }

        START_PARSE {

            ON_KEY("src", Src_) {
                return;
            }

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

        } END_PARSE

    private:
        const TString& Name_;
        NTvmAuth::TTvmId Src_ = 0;
        THolder<IModule> Slave_;
    };

public:
    TModule(const TModuleParams& mp)
        : TModuleBase(mp)
    {
        Config->ForEach(this);
        Y_VERIFY(ClientId_ > 0, "client_id must be specified");

        TvmClient_ = &TClientMap::Instance().GetClient(ClientId_);
    }

private:

    START_PARSE {

        ON_KEY("client_id", ClientId_) {
            return;
        }

        {
            TRouteParser(key, Routes_, DefaultRoute_, Copy(value->AsSubConfig()));
            return;
        }

    } END_PARSE

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

    TError DoRun(const TConnDescr& descr) const noexcept override {
        auto ptr = descr.Request->Headers().FindValues("x-ya-service-ticket");
        if (ptr != descr.Request->Headers().end() && !ptr->second.empty()) {
            TStringBuf ticket = ptr->second[0].AsStringBuf();
            auto result = TvmClient_->CheckServiceTicket(ticket);
            if (result) {
                auto routePtr = Routes_.find(result.GetSrc());
                if (routePtr != Routes_.end()) {
                    return routePtr->second->Run(descr);
                }
            }
        }

        if (DefaultRoute_) {
            return DefaultRoute_->Run(descr);
        }
        descr.ExtraAccessLog.SetSummary(GetHandle()->Name(), "no default route");

        return {};
    }

private:
    NTvmAuth::TTvmId ClientId_ = 0;
    NTvmAuth::TTvmClient* TvmClient_ = nullptr;
    THashMap<NTvmAuth::TTvmId, THolder<IModule>> Routes_;
    THolder<IModule> DefaultRoute_;
};

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