#include <util/generic/size_literals.h>

#include <robot/library/yt/static/table.h>
#include <yweb/antispam/common/owner/owner.h>

#include <wmconsole/version3/protos/digest.pb.h>
#include <wmconsole/version3/wmcutil/log.h>
#include <wmconsole/version3/wmcutil/yt/yt_runner.h>

#include "achievements.h"

namespace NWebmaster {

namespace {

    const char* F_HOST                      = "Host";
    const char* F_MASCOT_OWNER              = "MascotOwner";
    const char* F_TLD                       = "Tld";
    const char* F_BRANDS                    = "Brands";
    const char* F_HTTPS                     = "Https";
    const char* F_OFFICIAL                  = "Official";
    const char* F_OFFICIAL_AUTO             = "OfficialAuto";
    const char* F_OFFICIAL_AVIA             = "OfficialAvia";
    const char* F_OFFICAIL_CBR              = "OfficialCbr";
    const char* F_OFFICAIL_EMBASSY          = "OfficialEmbassy";
    const char* F_OFFICIAL_MFO              = "OfficialMfo";
    const char* F_OFFICIAL_SSD              = "OfficialSsd";
    const char* F_OFFICIAL_VISA             = "OfficialVisa";
    const char* F_OFFICIAL_YANDEX           = "OfficialYandex";
    const char* F_OFFICIAL_VISA_CENTER      = "OfficialVisaCenter";
    const char* F_POPULAR                   = "Popular";
    const char* F_SERVICE_CENTER            = "ServiceCenter";
    const char* F_SSL_BROKEN                = "SslBroken";
    const char* F_TASIX                     = "Tasix";
    const char* F_USER_SELECTION            = "UserSelection";
    const char* F_SPEED_DESKTOP             = "SpeedDesktop";
    const char* F_ABSOLUTE_SPEED_DESKTOP    = "AbsoluteSpeedDesktop";
    const char* F_SPEED_MOBILE              = "SpeedMobile";
    const char* F_SPEED_GRADE_MOBILE        = "SpeedGradeMobile";
    const char* F_ABSOLUTE_SPEED_MOBILE     = "AbsoluteSpeedMobile";
    const char* F_TURBO                     = "Turbo";

    const char* F_ACHIEVEMENTS              = "Achievements";
}

struct TAchievementsReducer : public NYT::IReducer<NYT::TTableReader<NYT::TNode>, NYT::TTableWriter<NYT::TNode>> {
    Y_SAVELOAD_JOB(WebmasterHostOwner)

    TAchievementsReducer() = default;

    TAchievementsReducer(const THashMap<TString, THashSet<TString>>& webmasterHostOwner)
        : WebmasterHostOwner(webmasterHostOwner)
    {
    }

public:
    THashMap<TString, THashSet<TString>> WebmasterHostOwner;

public:
    void addAchievement(proto::digest::AchievementsSnapshot *achievement, const NYT::TNode& row){
        if (!NYTUtils::IsNodeNull(row[F_BRANDS])) {
            achievement->set_brands(row[F_BRANDS].AsString());
        }
        if (!NYTUtils::IsNodeNull(row[F_HTTPS])) {
            achievement->set_https(row[F_HTTPS].AsBool());
        }
        if (!NYTUtils::IsNodeNull(row[F_OFFICIAL])) {
            achievement->set_official(row[F_OFFICIAL].AsBool());
        }
        if (!NYTUtils::IsNodeNull(row[F_OFFICIAL_AUTO])) {
            achievement->set_official_auto(row[F_OFFICIAL_AUTO].AsString());
        }
        if (!NYTUtils::IsNodeNull(row[F_OFFICIAL_AVIA])) {
            achievement->set_official_avia(row[F_OFFICIAL_AVIA].AsBool());
        }
        if (!NYTUtils::IsNodeNull(row[F_OFFICAIL_CBR])) {
            achievement->set_official_cbr(row[F_OFFICAIL_CBR].AsBool());
        }
        if (!NYTUtils::IsNodeNull(row[F_OFFICAIL_EMBASSY])) {
            achievement->set_official_embassy(row[F_OFFICAIL_EMBASSY].AsBool());
        }
        if (!NYTUtils::IsNodeNull(row[F_OFFICIAL_MFO])) {
            achievement->set_official_mfo(row[F_OFFICIAL_MFO].AsBool());
        }
        if (!NYTUtils::IsNodeNull(row[F_OFFICIAL_SSD])) {
            achievement->set_official_ssd(row[F_OFFICIAL_SSD].AsBool());
        }
        if (!NYTUtils::IsNodeNull(row[F_OFFICIAL_VISA])) {
            achievement->set_official_visa(row[F_OFFICIAL_VISA].AsBool());
        }
        if (!NYTUtils::IsNodeNull(row[F_OFFICIAL_YANDEX])) {
            achievement->set_official_yandex(row[F_OFFICIAL_YANDEX].AsBool());
        }
        if (!NYTUtils::IsNodeNull(row[F_OFFICIAL_VISA_CENTER])) {
            achievement->set_official_visa_center(row[F_OFFICIAL_VISA_CENTER].AsBool());
        }
        if (!NYTUtils::IsNodeNull(row[F_POPULAR])) {
            achievement->set_popular(row[F_POPULAR].AsUint64());
        }
        if (!NYTUtils::IsNodeNull(row[F_SERVICE_CENTER])) {
            achievement->set_service_center(row[F_SERVICE_CENTER].AsString());
        }
        if (!NYTUtils::IsNodeNull(row[F_SSL_BROKEN])) {
            achievement->set_ssl_broken(row[F_SSL_BROKEN].AsBool());
        }
        if (!NYTUtils::IsNodeNull(row[F_TASIX])) {
            achievement->set_tasix(row[F_TASIX].AsBool());
        }
        if (!NYTUtils::IsNodeNull(row[F_USER_SELECTION])) {
            achievement->set_user_selection(row[F_USER_SELECTION].AsUint64());
        }
        if (!NYTUtils::IsNodeNull(row[F_SPEED_DESKTOP])) {
            achievement->set_speed_desktop(row[F_SPEED_DESKTOP].AsBool());
        }
        if (!NYTUtils::IsNodeNull(row[F_ABSOLUTE_SPEED_DESKTOP])) {
            achievement->set_absolute_speed_desktop(row[F_ABSOLUTE_SPEED_DESKTOP].AsUint64());
        }
        if (!NYTUtils::IsNodeNull(row[F_SPEED_GRADE_MOBILE])) {
            achievement->set_speed_grade_mobile(row[F_SPEED_GRADE_MOBILE].AsUint64());
        }
        if (!NYTUtils::IsNodeNull(row[F_SPEED_MOBILE])) {
            achievement->set_speed_mobile(row[F_SPEED_MOBILE].AsBool());
        }
        if (!NYTUtils::IsNodeNull(row[F_ABSOLUTE_SPEED_MOBILE])) {
            achievement->set_absolute_speed_mobile(row[F_ABSOLUTE_SPEED_MOBILE].AsUint64());
        }
        if (!NYTUtils::IsNodeNull(row[F_TLD])) {
            achievement->set_tld(row[F_TLD].AsString());
        }
        if (!NYTUtils::IsNodeNull(row[F_TURBO])) {
            achievement->set_turbo(row[F_TURBO].AsBool());
        }
    }

    void Do(TReader* input, TWriter* output) override {

        proto::digest::SrcAchievements achievements;
        TString owner;
        for (; input->IsValid(); input->Next()) {
            const NYT::TNode& row = input->GetRow();
            owner = row[F_MASCOT_OWNER].AsString();
            addAchievement(achievements.add_achievements(), row);
        }

        if (THashSet<TString> * pHosts = WebmasterHostOwner.FindPtr(owner)) {
            TString stream;
            Y_PROTOBUF_SUPPRESS_NODISCARD achievements.SerializeToString(&stream);
            for (auto it = pHosts->begin(); it != pHosts->end(); it++) {
                output->AddRow(NYT::TNode()
                    (F_HOST, *it)
                    (F_MASCOT_OWNER, owner)
                    (F_ACHIEVEMENTS, stream)
                );
            }
        }
    }
};

REGISTER_REDUCER(TAchievementsReducer)

void PrepareAchievementsSource(NYT::IClientBasePtr clientSearch, const THashSet<TString>& webmasterHosts) {
    const auto &config = TConfig::CInstance();

    TMascotOwnerCanonizer MascotOwnerCanonizer;
    MascotOwnerCanonizer.LoadTrueOwners();

    THashMap<TString, THashSet<TString>> webmasterHostOwner;
    for (auto it = webmasterHosts.begin(); it != webmasterHosts.end(); it++) {
        TString owner = MascotOwnerCanonizer.GetHostOwner(*it);
        if (auto ownerPtr = webmasterHostOwner.FindPtr(owner)) {
            ownerPtr->insert(*it);
        } else {
            webmasterHostOwner[owner] = THashSet<TString>({ *it });
        }
    }

    const TString achievementsTable = NYTUtils::JoinPath(config.TABLE_DIGEST_SOURCE_ACHIEVEMENTS, NUtils::Date2StrTZ(Now().TimeT()));
    if (clientSearch->Exists(achievementsTable)) {
        LOG_INFO("table with achievements is already processed");
        return;
    }

    NYT::TTableSchema achievementsTableSchema;
    achievementsTableSchema.Strict(true);
    achievementsTableSchema.AddColumn(NYT::TColumnSchema().Name(F_HOST).Type(NYT::VT_STRING));
    achievementsTableSchema.AddColumn(NYT::TColumnSchema().Name(F_MASCOT_OWNER).Type(NYT::VT_STRING));
    achievementsTableSchema.AddColumn(NYT::TColumnSchema().Name(F_ACHIEVEMENTS).Type(NYT::VT_STRING));

    NYT::ITransactionPtr tx = clientSearch->StartTransaction();
    TOpRunner(tx)
        .InputNode(config.TABLE_SOURCE_ACHIEVEMENTS)
        .InputNode(config.TABLE_SOURCE_ACHIEVEMENTS_KUUB)
        .OutputNode(NYT::TRichYPath(achievementsTable).Schema(achievementsTableSchema))
        .MemoryLimit(4_GBs)
        .ReduceBy(F_MASCOT_OWNER)
        .Reduce(new TAchievementsReducer(webmasterHostOwner))
        .SortBy(F_HOST)
        .Sort(achievementsTable)
        ;

    tx->Commit();
}

} //namespace NWebmaster
