#pragma once

#include <util/generic/map.h>
#include <util/generic/deque.h>
#include <util/random/random.h>
#include <util/string/builder.h>

#include <kernel/yt/utils/yt_utils.h>
#include <mapreduce/yt/interface/client.h>

#include <wmconsole/version3/wmcutil/log.h>
#include <wmconsole/version3/wmcutil/path.h>
//#include <wmconsole/version3/wmcutil/yt/yt_utils.h>

namespace NWebmaster {

struct TYtTimeTrigger {
    static constexpr char const AttrName[] = "upload_time";

    TYtTimeTrigger(NYT::IClientBasePtr client, const TString &ytObj, int updatePeriodSeconds = 86400 * 7, const TString &attr = AttrName)
        : Client(client)
        , YtObject(ytObj)
        , Attr(attr)
        , UpdatePeriod(updatePeriodSeconds)
    {
        try {
            UpdateTimestamp = NJupiter::GetYtAttr(Client, YtObject, Attr).AsInt64();
        } catch (yexception &e) {
            LOG_WARN("TYtTimeTrigger, error: %s", e.what());
        };
    }

    bool NeedUpdate() const {
        return UpdateTimestamp < Now().TimeT() - UpdatePeriod;
    }

    void Update() {
        Update(Client);
    }

    void Update(NYT::IClientBasePtr client) {
        UpdateTimestamp = Now().TimeT();
        NJupiter::SetYtAttr(client, YtObject, Attr, UpdateTimestamp);
    }

public:
    NYT::IClientBasePtr Client;
    const TString YtObject;
    const TString Attr;
    const int UpdatePeriod = 0;
    time_t UpdateTimestamp = 0;
};

struct TYtSourceTrigger {
    static constexpr char const DefaultAttrName[] = "source_table";

    TYtSourceTrigger(NYT::IClientBasePtr client, const TString &targetTable, const TString attrName = DefaultAttrName)
        : TargetTable(targetTable)
        , AttrName(attrName)
    {
        try {
            Source = NJupiter::GetYtAttr(client, TargetTable, AttrName).AsString();
        } catch (yexception &e) {
            LOG_WARN("TYtSourceTrigger, error: %s", e.what());
            Source = ToString(RandomNumber<size_t>(Max<size_t>()));
        };
    }

    bool NeedUpdate(const TString &newSource) const {
        return Source != newSource;
    }

    void Update(NYT::IClientBasePtr client, const TString &newSource) {
        NJupiter::SetYtAttr(client, TargetTable, AttrName, newSource);
    }

public:
    TString TargetTable;
    TString AttrName;
    TString Source;
};

struct TYtValueTrigger {
    TYtValueTrigger(NYT::IClientBasePtr client, const TString &targetTable, const TString &attrName)
        : TargetTable(targetTable)
        , AttrName(attrName)
    {
        try {
            Source = NJupiter::GetYtAttr(client, TargetTable, AttrName);
        } catch (yexception &e) {
            LOG_WARN("TYtValueTrigger, error: %s", e.what());
        };
    }

    bool NeedUpdate(const NYT::TNode &newSource) const {
        return Source != newSource;
    }

    void Update(NYT::IClientBasePtr client, const NYT::TNode &newSource) {
        NJupiter::SetYtAttr(client, TargetTable, AttrName, newSource);
    }

public:
    TString TargetTable;
    TString AttrName;
    NYT::TNode Source;
};

struct TYtModificationTimeTrigger {
    static constexpr char const AttrModificationTime[] = "modification_time";
    TYtModificationTimeTrigger(const TString &targetTable)
        : TargetTable(targetTable)
    {
    }

    bool NeedUpdate(NYT::IClientBasePtr client, const TString& targetAttrName, const TString& sourceTable) const {
        try {
            const NYT::TNode &targetModTime = NJupiter::GetYtAttr(client, TargetTable, targetAttrName);
            const NYT::TNode &sourceModTime = NJupiter::GetYtAttr(client, sourceTable, AttrModificationTime);
            return sourceModTime != targetModTime;
        } catch (yexception &e) {
            LOG_WARN("TYtValueTrigger, error: %s", e.what());
            return true;
        };
    }

    bool NeedUpdate(NYT::IClientBasePtr sourceClient, NYT::IClientBasePtr targetClient, const TString& targetAttrName, const TString& sourceTable) const {
        try {
            const NYT::TNode &targetModTime = NJupiter::GetYtAttr(targetClient, TargetTable, targetAttrName);
            const NYT::TNode &sourceModTime = NJupiter::GetYtAttr(sourceClient, sourceTable, AttrModificationTime);
            return sourceModTime != targetModTime;
        } catch (yexception &e) {
            LOG_WARN("TYtValueTrigger, error: %s", e.what());
            return true;
        };
    }

    void Update(NYT::IClientBasePtr client, const TString& targetAttrName, const TString& sourceTable) {
        const NYT::TNode &sourceModTime = NJupiter::GetYtAttr(client, sourceTable, AttrModificationTime);
        NJupiter::SetYtAttr(client, TargetTable, targetAttrName, sourceModTime);
    }

    void Update(NYT::IClientBasePtr sourceClient, NYT::IClientBasePtr targetClient,
                const TString& targetAttrName, const TString& sourceTable) {
        const NYT::TNode &sourceModTime = NJupiter::GetYtAttr(sourceClient, sourceTable, AttrModificationTime);
        NJupiter::SetYtAttr(targetClient, TargetTable, targetAttrName, sourceModTime);
    }

public:
    TString TargetTable;
};

} //namespace NWebmaster
