#pragma once
#include <saas/rtyserver/config/config.h>
#include <saas/rtyserver/config/merger_config.h>
#include <util/datetime/base.h>
#include <library/cpp/object_factory/object_factory.h>

class IMergerPolicyBehavior {
protected:
    const TRTYServerConfig& Config;
public:
    using TFactory = NObjectFactory::TParametrizedObjectFactory<IMergerPolicyBehavior, NRTYServer::TMergerConfig::TMergerCheckPolicyType, const TRTYServerConfig&>;

    using TPtr = TAtomicSharedPtr<IMergerPolicyBehavior>;

    IMergerPolicyBehavior(const TRTYServerConfig& config)
        : Config(config)
    {

    }

    virtual ~IMergerPolicyBehavior() {
    }

    virtual TInstant GetWaitingDeadline() const = 0;
    virtual TInstant GetSleepingDeadline() const = 0;
    virtual bool IsDependOnExternalEvents() const = 0;
    virtual void OnNewIndex() {}
    virtual void OnMergerTaskFinish(ui32 /*newSegmentsCount*/, const TString& /*realmName*/) const {}
};

class TMergerPolicyNone: public IMergerPolicyBehavior {
private:
    static TFactory::TRegistrator<TMergerPolicyNone> Registrator;

public:

    TMergerPolicyNone(const TRTYServerConfig& config)
        : IMergerPolicyBehavior(config)
    {

    }

    TInstant GetWaitingDeadline() const override {
        return TInstant::Max();
    }

    TInstant GetSleepingDeadline() const override {
        return TInstant::Zero();
    }

    bool IsDependOnExternalEvents() const override {
        return false;
    }
};

class TMergerPolicyTime: public TMergerPolicyNone {
private:
    TDuration CheckInterval;
    static TFactory::TRegistrator<TMergerPolicyTime> Registrator;

public:

    TMergerPolicyTime(const TRTYServerConfig& config)
        : TMergerPolicyNone(config)
        , CheckInterval(TDuration::MilliSeconds(Config.GetMergerConfig().TimingCheckIntervalMilliseconds))
    {

    }

    TInstant GetWaitingDeadline() const override {
        return Now() + CheckInterval;
    }
};

class TMergerPolicyNewIndexSimple: public IMergerPolicyBehavior {
private:
    static TFactory::TRegistrator<TMergerPolicyNewIndexSimple> Registrator;
public:
    TMergerPolicyNewIndexSimple(const TRTYServerConfig& config)
        : IMergerPolicyBehavior(config)
    {

    }

    TInstant GetWaitingDeadline() const override {
        return TInstant::Max();
    }

    TInstant GetSleepingDeadline() const override {
        return TInstant::Zero();
    }

    bool IsDependOnExternalEvents() const override {
        return true;
    }

};

class TMergerPolicyNewIndex: public TMergerPolicyNewIndexSimple {
private:
    TDuration Deferment;
    TInstant SleepingDeadline;
    static TFactory::TRegistrator<TMergerPolicyNewIndex> Registrator;

public:
    TMergerPolicyNewIndex(const TRTYServerConfig& config)
        : TMergerPolicyNewIndexSimple(config)
        , Deferment(TDuration::Seconds(config.GetMergerConfig().NewIndexDefermentSec))
    {
    }

    TInstant GetWaitingDeadline() const override {
        return TInstant::Max();
    }

    TInstant GetSleepingDeadline() const override {
        return SleepingDeadline;
    }

    void OnNewIndex() override {
        if (Deferment) {
            SleepingDeadline = TInstant::MicroSeconds(Max(SleepingDeadline.GetValue(), (Now() + Deferment).GetValue()));
        }
    }

    bool IsDependOnExternalEvents() const override {
        return true;
    }
};

class TMergerPolicyContinuous: public TMergerPolicyNewIndex {
private:
    static TFactory::TRegistrator<TMergerPolicyContinuous> Registrator;
public:
    TMergerPolicyContinuous(const TRTYServerConfig& config)
        : TMergerPolicyNewIndex(config)
    {

    }

    void OnMergerTaskFinish(ui32 newSegmentsCount, const TString& realmName) const override;
};

