#pragma once

#include "metrics.h"
#include <saas/library/histogram/histogram.h>

#include <library/cpp/json/json_value.h>
#include <library/cpp/mediator/messenger.h>

#include <util/datetime/base.h>
#include <util/digest/fnv.h>
#include <util/generic/map.h>
#include <util/generic/maybe.h>
#include <util/generic/set.h>
#include <util/generic/string.h>

class TRTYMessageException: public IMessage {
public:
    enum TExceptionCase { ecMergerDoTask };
private:
    TExceptionCase ExceptionCase;
    ui32 Handle;
public:
    TRTYMessageException(TExceptionCase exceptionCase) {
        ExceptionCase = exceptionCase;
        Handle = 0;
    }

    TRTYMessageException(TExceptionCase exceptionCase, ui32 handle) {
        ExceptionCase = exceptionCase;
        Handle = handle;
    }

    ui32 GetHandle() const {
        return Handle;
    }

    TExceptionCase GetExceptionCase() const {
        return ExceptionCase;
    }
};

class TMessageRemoveSpecial: public IMessage {
public:
    enum TRemoveType { rtRemoveAll, rtRemoveKps };
private:
    TRemoveType Type;
    ui64 Kps;
    ui64 DocsRemoved = 0;
    ui64 NotSearchDocuments = 0;
public:
    TMessageRemoveSpecial()
        : Type(rtRemoveAll)
    {}

    explicit TMessageRemoveSpecial(ui64 kps)
        : Type(rtRemoveKps)
        , Kps(kps)
    {}

    ui64 GetKps() const {
        return Kps;
    }

    TRemoveType GetType() const {
        return Type;
    }

    void AddNotSearchDocuments(ui64 docsCount) {
        NotSearchDocuments += docsCount;
    }

    void AddDocuments(ui64 docsCount) {
        DocsRemoved += docsCount;
    }

    ui64 GetDocuments() const {
        return DocsRemoved;
    }
};

class TMessageCreateMergerTasks: public IMessage {
};

class TMessageGetMergerTaskCount: public IMessage {
private:
    const unsigned Shard;
    const TString RealmName;
    size_t Count;
public:
    explicit TMessageGetMergerTaskCount(unsigned shard = Max<unsigned>(), const TString& realmName = "")
        : Shard(shard)
        , RealmName(realmName)
        , Count(0)
    {}
    void IncCount(unsigned shard, const TString& realmName) {
        if ((RealmName == realmName || RealmName == "") && (Shard == shard || Shard == Max<unsigned>())) {
            ++Count;
        }
    }
    size_t GetCount() const { return Count; }
};

class TMessageRemoveDeadDocuments: public IMessage {
private:
    ui32 TimeInMinutesGM;
public:
    TMessageRemoveDeadDocuments(ui32 timeInMinutesGM): TimeInMinutesGM(timeInMinutesGM) {}
    inline ui32 GetTimeInMinutesGM() {
        return TimeInMinutesGM;
    }
};

class TMessageRemoveEmptyIndexes: public IMessage {
};

class TMessageSendRepairInfo: public IMessage {
private:
    TString TempDir;
public:
    explicit TMessageSendRepairInfo(TString tempDir)
        : TempDir(std::move(tempDir))
    {}

    const TString& GetTempDir() {
        return TempDir;
    }
};

class TMessageSearchServerControl: public IMessage {
public:
    enum TSearchServerControlAction { sscaCheck = 0, sscaStart, sscaStop, sscaEnd };
private:
    const TSearchServerControlAction Action;
    bool Running;
public:
    explicit TMessageSearchServerControl(TSearchServerControlAction action = sscaCheck)
        : Action(action)
        , Running(false)
    {}
    bool ShouldStart() const {
        return Action == sscaStart;
    }
    bool ShouldStop() const {
        return Action == sscaStop;
    }
    void SetRunning(bool running) {
        Running = running;
    }
    bool IsRunning() const {
        return Running;
    }
};

class TMessageMemorySearchControl: public IMessage {
public:
    enum TMemorySearchControlAction { mscCheck = 0, mscEnable, mscDisable, mscEnd };
private:
    const TMemorySearchControlAction Action;
    bool Enabled;
public:
    explicit TMessageMemorySearchControl(TMemorySearchControlAction action = mscCheck)
        : Action(action)
        , Enabled(false)
    {}
    bool ShouldEnable() const {
        return Action == mscEnable;
    }
    bool ShouldDisable() const {
        return Action == mscDisable;
    }
    void SetEnabled(bool enabled) {
        Enabled = enabled;
    }
    bool IsEnabled() const {
        return Enabled;
    }
};

class TMessageIndexingControl: public IMessage {
public:
    enum EControlAction { Enable, Disable, GetStatus };
public:
    TMessageIndexingControl(EControlAction action)
        : Action(action)
        , Running(false)
    {}
    EControlAction GetAction() const {
        return Action;
    }
    bool IsRunning() const {
        return Running;
    }
    void SetRunning(bool running) {
        Running = running;
    }
private:
    EControlAction Action;
    bool Running;
};

// XXX
class TMessageGetUsedFactors: public IMessage {
private:
    NJson::TJsonValue FactorsConfig;
    NJson::TJsonValue FactorsUsage;
public:
    TMessageGetUsedFactors() {
    };

    const NJson::TJsonValue& GetFactorsConfig() const {
        return FactorsConfig;
    }

    const NJson::TJsonValue& GetFactorsUsage() const {
        return FactorsUsage;
    }

    void SetFactorsConfig(const NJson::TJsonValue& value) {
        FactorsConfig = value;
    }

    void SetFactorsUsage(const NJson::TJsonValue& value) {
        FactorsUsage = value;
    }
};

class TMessageDeferredUpdaterActivated: public IMessage {
public:
    enum class EDeferredUpdateMoment {dumMerge, dumCloseIndex};

    TMessageDeferredUpdaterActivated(EDeferredUpdateMoment moment)
        : DeferredUpdateMoment(moment)
    {

    }

    const EDeferredUpdateMoment DeferredUpdateMoment;
};

class TMessageDeferredUpdatesNotification: public IMessage {
public:
    explicit TMessageDeferredUpdatesNotification(ui32 countUpdates)
        : CountUpdates(countUpdates)
    {}

    const ui32 CountUpdates;
};

class TMessageRemoveCatchInfo: public IMessage {
private:
    TString TempName;
    TSet<TString> ExcludedIndexes;
public:
    TMessageRemoveCatchInfo(const TString& tempName, const TSet<TString>& excludedIndexes = TSet<TString>())
        : TempName(tempName)
        , ExcludedIndexes(excludedIndexes)
    {
    }

    const TString& GetTempName() const {
        return TempName;
    }

    const TSet<TString>& GetExcludedIndexes() const {
        return ExcludedIndexes;
    }

    ui32 GetHash() const {
        return FnvHash<ui32>(TempName.data(), TempName.size());
    }
};

class TMessageDocumentIndexed: public IMessage {
public:
    TMessageDocumentIndexed(const TString& dir, const TString& url, const TString& type)
        : Dir(dir)
        , Url(url)
        , Type(type)
    {};
    const TString& GetDir() {return Dir;}
    const TString& GetUrl() {return Url;}
    const TString& GetType() {return Type;}

private:
    const TString& Dir;
    const TString& Url;
    const TString& Type;
};

class TMessageSwitchKps: public IMessage {
public:
    TMessageSwitchKps(ui64 newKps)
        : NewKps(newKps)
    {}
    const ui64& GetNewKps() {return NewKps;}
private:
    ui64 NewKps;
};

class TMessageGetServerHealth : public IMessage {
public:
    ui32 CrashCount = 0;
    ui32 MergeFailedCount = 0;
    ui32 LastMergeTime = 0;
    ui32 LastMergeElapsedTimed = 0;
    ui32 ActiveDiskIndexCount = 0;
    ui32 IdleDiskIndexCount = 0;
    bool SearchServerRunning = false;
    bool Active = false;
    bool NonRunnable = false;
    bool MustBeAlive = true;
    float RequestDropRate = 0.0f;
    ui32 BanTimestamp = 0;
    ui32 SlotTimestamp = 0;
    TRTYRecoveryStage RecoveryStage = TRTYRecoveryStage::ServerNotExist;
    TString NonRunnableDetails;

    // ShardInfo is a set (to avoid duplicate info) of (Shard, NumShards) pairs.
    TSet<std::pair<ui32, ui32>> ShardInfo;

    TMap<TString, TString> ComponentDigests;
};

class TMessageGetDocfetcherStatus: public IMessage {
public:
    bool DocfetcherPresent = false;

    NJson::TJsonValue Status;
    ui64 QueueSize = 0;
    ui64 DocsIndexing = 0;
    ui64 Timestamp = 0;
    bool HasDocsInDistributor = false;

    NJson::TJsonValue Replies;
};

class TMessagePessimizeDocfetcherBan : public IMessage {
public:
    TMessagePessimizeDocfetcherBan(bool pessimize = true)
        : Pessimize(pessimize)
    {
    }
    bool Pessimize = true;
};

struct TMessageGetIndexHistogram: public IMessage {
    NRTYServer::THistograms Histograms;
};

struct TMessageGetState : public IMessage {
    ui32 State = 0;
};

class TUniversalAsyncMessage: public IMessage {
public:
    TUniversalAsyncMessage(const TString& type)
        : Type(type)
    {}
    const TString& GetType() const {
        return Type;
    }
private:
    const TString Type;
};

class TMessageNotifyDiagEvent final: public TUniversalAsyncMessage {
public:
    using TUniversalAsyncMessage::TUniversalAsyncMessage;
};

struct TMessagePauseDocfetcher: public IMessage {
};

struct TMessageContinueDocfetcher: public IMessage {
};

struct TMessageSetDocfetcherDatacentersStatus : public IMessage {
    TDuration BlockTime;
    TVector<TString> Datacenters;
    bool Failed;
};

struct TMessageItsOverride: public IMessage {
public:
    TString Key;
    TMaybe<TString> Value;
};

struct TDocSearchInfo;

struct TMessageUpdateInvoked: public IMessage {
    const TDocSearchInfo& DocSearchInfo;

    TMessageUpdateInvoked(const TDocSearchInfo& info)
        : DocSearchInfo(info)
    {}
};

struct TMessageSlowUpdateInvoked: public TMessageUpdateInvoked {
    using TMessageUpdateInvoked::TMessageUpdateInvoked;
};

struct TMessageFastUpdateInvoked: public TMessageUpdateInvoked {
    ui32 Count;

    TMessageFastUpdateInvoked(const TDocSearchInfo& info, ui32 count)
        : TMessageUpdateInvoked(info)
        , Count(count)
    {}
};

struct TMessageOnMergerTaskStart : public IMessage {
};

struct TMessageOnMergerTaskFinish : public IMessage {
};

struct TMessageOnSyncStart : public IMessage {
    const TString ResourceName;

    TMessageOnSyncStart(const TString& resourceName)
        : ResourceName(resourceName)
    {}
};

struct TMessageOnSyncFinish : public IMessage {
};
