#include <util/draft/date.h>
#include <util/generic/queue.h>

#include <library/cpp/getopt/last_getopt.h>
#include <mapreduce/yt/interface/constants.h>

#include <kernel/yt/attrs/attrs.h>

#include <robot/library/yt/static/command.h>
#include <robot/library/yt/static/table.h>
#include <robot/library/yt/static/tags.h>

#include <wmconsole/version3/wmcutil/log.h>

#include <wmconsole/version3/processors/achievements/conf/config.h>
#include <wmconsole/version3/processors/achievements/protos/achievements.pb.h>
#include <wmconsole/version3/processors/achievements/protos/sources.pb.h>
#include "task_merge.h"

namespace NWebmaster {

using namespace NJupiter;

TInputTag<NProto::TPrepSearchBase> PrepSearchBaseInputTag       (1);
TInputTag<NProto::TSrcAchievements> SrcAchievementsInputTag     (2);
TInputTag<NProto::TPrepIks> PrepIksInputTag                     (3);
TInputTag<NProto::TPrepVideohostings> PrepVideohostingsInputTag (4);

TOutputTag<NProto::TAchievements> AchievementsOutputTag         (1);

//ReduceBy MascotOwner
struct TMergeAchievementsJoinReducer : public TTaggedReducer {
public:
    void DoTagged(TTagedReader reader, TTagedWriter writer) override {
        TMaybe<NProto::TPrepSearchBase> prepSearchBaseMaybe = reader.GetSingleRowMaybe(PrepSearchBaseInputTag);
        TMaybe<NProto::TPrepIks> prepIksMaybe = reader.GetSingleRowMaybe(PrepIksInputTag);
        TMaybe<NProto::TPrepVideohostings> prepVideohostingsMaybe = reader.GetSingleRowMaybe(PrepVideohostingsInputTag);

        if (!reader.IsValid()) {
            return;
        }

        NProto::TAchievements dstMsg;
        for (auto row : reader.GetRows(SrcAchievementsInputTag)) {
            dstMsg.SetMascotOwner(row.GetMascotOwner());

            if (row.HasAbsoluteSpeedDesktop()) {
                dstMsg.SetAbsoluteSpeedDesktop(row.GetAbsoluteSpeedDesktop());
            }
            if (row.HasAbsoluteSpeedMobile()) {
                dstMsg.SetAbsoluteSpeedMobile(row.GetAbsoluteSpeedMobile());
            }
            if (row.HasBrands()) {
                dstMsg.SetBrands(row.GetBrands());
            }
            if (row.HasHttps()) {
                dstMsg.SetHttps(row.GetHttps());
            }
            if (row.HasOfficial()) {
                dstMsg.SetOfficial(row.GetOfficial());
            }
            if (row.HasOfficialAuto()) {
                dstMsg.SetOfficialAuto(row.GetOfficialAuto());
            }
            if (row.HasOfficialAvia()) {
                dstMsg.SetOfficialAvia(row.GetOfficialAvia());
            }
            if (row.HasOfficialCbr()) {
                dstMsg.SetOfficialCbr(row.GetOfficialCbr());
            }
            if (row.HasOfficialCbrRecalled()) {
                dstMsg.SetOfficialCbrRecalled(row.GetOfficialCbrRecalled());
            }
            if (row.HasOfficialCbrfBki()) {
                dstMsg.SetOfficialCbrfBki(row.GetOfficialCbrfBki());
            }
            if (row.HasOfficialCbrfKopurcb()) {
                dstMsg.SetOfficialCbrfKopurcb(row.GetOfficialCbrfKopurcb());
            }
            if (row.HasOfficialCbrfProf()) {
                dstMsg.SetOfficialCbrfProf(row.GetOfficialCbrfProf());
            }
            if (row.HasOfficialCbrfStock()) {
                dstMsg.SetOfficialCbrfStock(row.GetOfficialCbrfStock());
            }
            if (row.HasOfficialEmbassy()) {
                dstMsg.SetOfficialEmbassy(row.GetOfficialEmbassy());
            }
            if (row.HasOfficialMfo()) {
                dstMsg.SetOfficialMfo(row.GetOfficialMfo());
            }
            if (row.HasOfficialSsd()) {
                dstMsg.SetOfficialSsd(row.GetOfficialSsd());
            }
            if (row.HasOfficialYandex()) {
                dstMsg.SetOfficialYandex(row.GetOfficialYandex());
            }
            if (row.HasOfficialVisaCenter()) {
                dstMsg.SetOfficialVisaCenter(row.GetOfficialVisaCenter());
            }
            if (row.HasOfficialTheatre()) {
                dstMsg.SetOfficialTheatre(row.GetOfficialTheatre());
            }
            if (row.HasOfficialCbrfNpf()) {
                dstMsg.SetOfficialCbrfNpf(row.GetOfficialCbrfNpf());
            }
            if (row.HasPopular()) {
                dstMsg.SetPopular(row.GetPopular());
            }
            if (row.HasServiceCenter()) {
                dstMsg.SetServiceCenter(row.GetServiceCenter());
            }
            if (row.HasSpeedDesktop()) {
                dstMsg.SetSpeedDesktop(row.GetSpeedDesktop());
            }
            if (row.HasSpeedGradeDesktop()) {
                dstMsg.SetSpeedGradeDesktop(row.GetSpeedGradeDesktop());
            }
            if (row.HasSpeedGradeMobile()) {
                dstMsg.SetSpeedGradeMobile(row.GetSpeedGradeMobile());
            }
            if (row.HasSpeedMobile()) {
                dstMsg.SetSpeedMobile(row.GetSpeedMobile());
            }
            if (row.HasSslBroken()) {
                dstMsg.SetSslBroken(row.GetSslBroken());
            }
            if (row.HasTasix()) {
                dstMsg.SetTasix(row.GetTasix());
            }
            if (row.HasTld()) {
                dstMsg.SetTld(row.GetTld());
            }
            if (row.HasTurbo()) {
                dstMsg.SetTurbo(row.GetTurbo());
            }
            if (row.HasUserSelection()) {
                dstMsg.SetUserSelection(row.GetUserSelection());
            }
            if (prepSearchBaseMaybe.Defined()) {
                dstMsg.SetWMDocsOnSearch(prepSearchBaseMaybe.GetRef().GetDocsOnSearch());
            }
            if (prepIksMaybe.Defined()){
                dstMsg.SetIks(prepIksMaybe.GetRef().GetIks());
            }
            if (row.HasTurboWm()) {
                dstMsg.SetTurboWm(row.GetTurboWm());
            }
            if (prepVideohostingsMaybe.Defined()) {
                dstMsg.SetVideohostRating(prepVideohostingsMaybe.GetRef().GetGrade());
            }

            writer.AddRow(dstMsg, AchievementsOutputTag);
        }
    }
};

REGISTER_REDUCER(TMergeAchievementsJoinReducer)

void PrepareAchievements(NYT::IClientBasePtr tx) {
    const auto &cfg = NAchievements::TConfig::CInstance();
    const NYT::TSortColumns KEYS_SORT = {"MascotOwner", "Tld"};

    LOG_INFO("achievements, preparing");
    DoParallel(
        TSortCmd<NProto::TSrcAchievements>(tx)
            .Input(TTable<NProto::TSrcAchievements>(tx, cfg.TABLE_SOURCE_ACHIEVEMENTS))
            .Output(TTable<NProto::TSrcAchievements>(tx, cfg.TABLE_ACHIEVEMENTS_PREP_RU).PreCreate())
            .OperationWeight(cfg.OPERATION_WEIGHT)
            .By(KEYS_SORT),

        TSortCmd<NProto::TSrcAchievements>(tx)
            .Input(TTable<NProto::TSrcAchievements>(tx, cfg.TABLE_SOURCE_ACHIEVEMENTS_KUUB))
            .Output(TTable<NProto::TSrcAchievements>(tx, cfg.TABLE_ACHIEVEMENTS_PREP_KUUB).PreCreate())
            .OperationWeight(cfg.OPERATION_WEIGHT)
            .By(KEYS_SORT)
    );
    LOG_INFO("achievements, preparing - done");
}

void MergeAchievements(NYT::ITransactionPtr tx) {
    const auto &cfg = NAchievements::TConfig::CInstance();
    const NYT::TSortColumns KEYS_REDUCE = {"MascotOwner"};
    const NYT::TSortColumns KEYS_SORT = {"MascotOwner", "Tld"};

    LOG_INFO("achievements, merging");
    DoParallel(
        TReduceCmd<TMergeAchievementsJoinReducer>(tx)
            .Input(TTable<NProto::TPrepSearchBase>(tx, cfg.TABLE_ACHIEVEMENTS_PREP_SEARCHBASE), PrepSearchBaseInputTag)
            .Input(TTable<NProto::TPrepIks>(tx, cfg.TABLE_ACHIEVEMENTS_PREP_IKS), PrepIksInputTag)
            .Input(TTable<NProto::TPrepVideohostings>(tx, cfg.TABLE_ACHIEVEMENTS_PREP_VIDEOHOSTINGS), PrepVideohostingsInputTag)
            .Input(TTable<NProto::TSrcAchievements>(tx, cfg.TABLE_ACHIEVEMENTS_PREP_RU), SrcAchievementsInputTag)
            .Output(TTable<NProto::TAchievements>(tx, cfg.TABLE_ACHIEVEMENTS_EXPORT_RU), AchievementsOutputTag)
            .OperationWeight(cfg.OPERATION_WEIGHT)
            .ReduceBy(KEYS_REDUCE),
        TReduceCmd<TMergeAchievementsJoinReducer>(tx)
            .Input(TTable<NProto::TPrepSearchBase>(tx, cfg.TABLE_ACHIEVEMENTS_PREP_SEARCHBASE), PrepSearchBaseInputTag)
            .Input(TTable<NProto::TPrepVideohostings>(tx, cfg.TABLE_ACHIEVEMENTS_PREP_VIDEOHOSTINGS), PrepVideohostingsInputTag)
            .Input(TTable<NProto::TSrcAchievements>(tx, cfg.TABLE_ACHIEVEMENTS_PREP_KUUB), SrcAchievementsInputTag)
            .Output(TTable<NProto::TAchievements>(tx, cfg.TABLE_ACHIEVEMENTS_EXPORT_KUUB), AchievementsOutputTag)
            .OperationWeight(cfg.OPERATION_WEIGHT)
            .ReduceBy(KEYS_REDUCE)
    );

    DoParallel(
        TSortCmd<NProto::TAchievements>(tx, TTable<NProto::TAchievements>(tx, cfg.TABLE_ACHIEVEMENTS_EXPORT_RU))
            .By(KEYS_SORT),
        TSortCmd<NProto::TAchievements>(tx, TTable<NProto::TAchievements>(tx, cfg.TABLE_ACHIEVEMENTS_EXPORT_KUUB))
            .By(KEYS_SORT)
    );
    LOG_INFO("achievements, merging - done");
}

int TaskMerge(int, const char **) {
    const auto &cfg = NAchievements::TConfig::CInstance();

    NYT::IClientPtr client = NYT::CreateClient(cfg.MR_SERVER_HOST);
    NYTUtils::CreatePath(client, cfg.TABLE_ACHIEVEMENTS_PREP_ROOT);

    const TString sourceTimestamp = GetYtAttr(client, cfg.TABLE_SOURCE_ACHIEVEMENTS, TYtAttrName::ModificationTime).AsString();
    TString updatedTimestamp;
    try {
        updatedTimestamp = GetYtAttr(client, cfg.TABLE_ACHIEVEMENTS_EXPORT_RU, cfg.ATTR_UPDATE_SOURCE).AsString();
    } catch (yexception &) {
    }

    if (updatedTimestamp == sourceTimestamp) {
        LOG_INFO("achievements, already updated");
        return 0;
    }

    NYT::ITransactionPtr tx = client->StartTransaction();
    PrepareAchievements(tx);
    MergeAchievements(tx);
    SetYtAttr(tx, cfg.TABLE_ACHIEVEMENTS_EXPORT_RU, cfg.ATTR_UPDATE_SOURCE, sourceTimestamp);
    tx->Commit();

    return 0;
}

} //namespace NWebmaster
