#include "context.h"
#include "ctx_creator.h"

#include <saas/searchproxy/common/cgi.h>
#include <search/grouping/groupinfo.h>

#include <library/cpp/scheme/scheme.h>

#include <util/generic/string.h>
#include <library/cpp/cgiparam/cgiparam.h>

using namespace NSaas::NExperiments;

namespace {
    const TString ControlCtxName = "Original";

    void AddContext(TCgiParameters& cgi, const TString& name, const TSourceParams& params) {
        TCgiParameters rawCtx;
        rawCtx.InsertEscaped("context_name", name);
        for (auto&& param : params) {
            rawCtx.InsertEscaped(param.Name, param.Value);
        }

        cgi.InsertUnescaped("raw_ctx", rawCtx.Print());
    }

    void EnableTdi4All(NSc::TValue& scheme) {
        scheme["Tdi4All"]["Enabled"] = true;
        scheme["Tdi4All"]["RearrOff"] = true;
    }

    void AddTdi4AllScheme(NSc::TValue& scheme, const TString& attr, const TString& contextA, const TString& contextB) {
        NSc::TValue& g = scheme["Tdi4All"]["Groupings"][attr];
        g["Enabled"] = true;
        g["A"]["CtxName"] = contextA;
        g["B"]["CtxName"] = contextB;
    }

    void AddAbtScheme(NSc::TValue& scheme, const TString& attr, const TString& contextA, const TString& contextB) {
        AddTdi4AllScheme(scheme, attr, contextA, contextB);
        scheme["Tdi4All"][attr]["Cfg"]["Mode"] = "abt";
        scheme["Tdi4All"][attr]["Cfg"]["Abt"]["Exp"] = true;
    }

    void AddExperiment(TCgiParameters& cgi, NSc::TValue& scheme, const TString& contextA, const TString& contextB, const TExperimentParams& e) {
        AddContext(cgi, contextA, e[A]);
        AddContext(cgi, contextB, e[B]);

        TVector<TString> groupings = e.Attributes;
        if (groupings.empty()) {
            for (size_t i = 0; i < cgi.NumOfValues("g"); ++i) {
                TGroupingParams g(cgi.Get("g", i));
                groupings.push_back(g.gAttr);
            }
        }
        if (groupings.empty()) {
            groupings.push_back(TString()); // it's GM_FLAT
        }

        for (auto&& g : groupings) {
            switch (e.Type) {
            case TDI:
                EnableTdi4All(scheme);
                AddTdi4AllScheme(scheme, g, contextA, contextB);
                break;
            case ABT:
                EnableTdi4All(scheme);
                AddAbtScheme(scheme, g, contextA, contextB);
                break;
            default:
                throw TCtxCreationException() << "unsupported experiment type " << int(e.Type);
            }
        }
    }

    const TExperimentParams& GetExperimentParams(const TUserCtx& userContext, ui64 id) {
        auto p = userContext.Experiments.find(id);
        if (p == userContext.Experiments.end()) {
            throw yexception() << "Cannot find experiment " << id << " info";
        }
        return p->second;
    }
}

void TContextCreator::Process(const TUserCtx& userContext, TCgiParameters& cgi) const {
    if (!userContext.HasExperiment()) {
        return;
    }

    NSc::TValue scheme;
    scheme["RTYRedirect"]["RearrOff"] = true;

    for (auto&& bucket : userContext.Buckets) {
        const ui64 id = bucket.Id;
        const TExperimentParams& e = GetExperimentParams(userContext, id);
        const bool single = userContext.Buckets.size() == 1;

        const TString contextA = single ? ControlCtxName : ("A" + ToString(id));
        const TString contextB = single ? "MAIN" : ("B" + ToString(id));

        AddExperiment(cgi, scheme, contextA, contextB, e);
    }

    cgi.InsertUnescaped("rearr", "scheme_Local=" + scheme.ToJson());
    cgi.InsertUnescaped(NSearchProxyCgi::test_buckets, ToString(userContext.Buckets));
}
