#pragma once

#include <util/datetime/base.h>
#include <util/draft/date.h>
#include <util/string/printf.h>
#include <library/cpp/clickhouse/client/block.h>
#include <library/cpp/clickhouse/client/client.h>
#include <robot/library/yt/static/command.h>
#include <wmconsole/version3/library/jupiter/jupiter.h>
#include <wmconsole/version3/processors/user_sessions/conf/yt.h>
#include <wmconsole/version3/processors/user_sessions/protos/user_sessions.pb.h>
#include <wmconsole/version3/wmcutil/log.h>

#include "table_config.h"

namespace NWebmaster {
namespace NUserSessions {

using namespace NJupiter;

struct TMetrikaRobotHits {
    const static unsigned int METRIKA_SHARD_COUNT = 2;

    Y_SAVELOAD_DEFINE(Hits)

    struct THits {
        Y_SAVELOAD_DEFINE(GoodHits, AllHits)

        void Add(ui32 isRobot) {
            if (isRobot == 0) {
                GoodHits += 1.0f;
            }
            AllHits += 1.0;
        }

        bool IsRobot() const {
            if (AllHits == 0.0) {
                return false;
            }
            return (GoodHits / AllHits) < 0.4f;
        }

    public:
        float GoodHits = 0;
        float AllHits = 0;
    };

public:
    void Add(const TString &uid, ui32 /*uidType*/, ui32 isRobot, ui32 /*timestamp*/) {
        Hits[uid].Add(isRobot);
    }

    void Add(ui64 uid, ui32 uidType, ui32 isRobot, ui32 timestamp) {
        const TString uidStr = "y" + ToString(uid);
        Add(uidStr, uidType, isRobot, timestamp);
    }

    bool FindIsRobot(const TString &uid, ui32 &isRobot) const {
        const auto it = Hits.find(uid);
        if (it == Hits.end()) {
            return false;
        }
        isRobot = it->second.IsRobot() ? 1 : 0;
        return true;
    }

    void InitFromYT(NYT::IClientBasePtr client, const TString &inputTable) {
        auto reader = TTable<NProto::TMetrikaRobot>(client, inputTable).GetReader();
        for (; reader->IsValid(); reader->Next()) {
            const auto &row = reader->GetRow();
            Add(row.GetUserID(), row.GetUserIDType(), row.GetIsRobot(), row.GetTimestamp());
        }
    }

    void Init(const TDeque<NProto::TMetrikaRobot> &robots) {
        for (const auto &row : robots) {
            Add(row.GetUserID(), row.GetUserIDType(), row.GetIsRobot(), row.GetTimestamp());
        }
    }

    static void LoadFromMetrikaCH(const TString &dateStr, TDeque<NProto::TMetrikaRobot> &robots);
    static void LoadFromMetrikaCH(const TString &dateStr, TDeque<NProto::TMetrikaRobot> &robots, unsigned int shard);

    static void SaveToYT(NYT::IClientBasePtr client, const TDeque<NProto::TMetrikaRobot> &robots, const TString &outputTable);

public:
    THashMap<TString, THits> Hits;
};

void LoadMetrikaRobots(const TTableConfig &ttcfg, TMetrikaRobotHits &robotHits);

} //namespace NUserSessions
} //namespace NWebmaster
