#pragma once

#include <saas/protos/shards.pb.h>
#include <saas/util/types/interval.h>

#include <library/cpp/retry/retry.h>
#include <library/cpp/object_factory/object_factory.h>

#include <util/datetime/base.h>
#include <util/generic/vector.h>

struct IStopCondition {
    virtual ~IStopCondition() = default;

    // expected to be thread-safe
    virtual bool Stopped() const = 0;

    static const IStopCondition& NonStop();
};

struct ISnapshotFilter {
    virtual ~ISnapshotFilter() = default;
    virtual bool IsAccepted(TInstant snapshotTimestamp) const = 0;

    using TPtr = THolder<ISnapshotFilter>;

    static TPtr AcceptAll();
    static TPtr TimestampRange(TInstant newerThan, TInstant olderOrEqualTo);
    static TPtr FixedTimestamp(TInstant timestamp);
};

class IIndexSnapshotManager {
public:
    struct TSnapshot {
        TInstant Timestamp;
        NRTYServer::TShards Shards;
    };
    using TSnapshots = TVector<TSnapshot>;
    using TShardResource = NRTYServer::TShardResource;
    using TShardResources = TVector<TShardResource>;
    using TGroupedShardResources = TMap<TInstant, TShardResources>;

    struct TContext {
        TString Server;
        TString Path;
        TString Token = TString();
        TRetryOptions RetryOptions = TRetryOptions();
        TString YTHosts = TString();
        bool UseGroups = false;
    };

    using TFactory = NObjectFactory::TParametrizedObjectFactory<IIndexSnapshotManager, TString, TContext>;

public:
    IIndexSnapshotManager(TContext context);
    virtual ~IIndexSnapshotManager() = default;

    void PublishSnapshot(const TSnapshot& snapshot);
    std::tuple<TSnapshots, bool> GetSnapshots(TInstant newerThan = TInstant::Zero(), TInstant olderThanOrEqualTo = TInstant::Max(), const IStopCondition& stopper = IStopCondition::NonStop());
    TSnapshots GetSnapshotsFixed(TInstant timestamp, const IStopCondition& stopper = IStopCondition::NonStop());

    TShardResources GetResourcesForShard(NUtil::TInterval<ui64> shard, TInstant newerThan = TInstant::Zero(), TInstant olderThanOrEqualTo = TInstant::Max(), const IStopCondition& stopper = IStopCondition::NonStop());
    TShardResources GetResourcesForShard(NUtil::TInterval<ui64> shard, TDuration maxAge, const IStopCondition& stopper = IStopCondition::NonStop());

    TShardResources GetResourceForShardFixed(NUtil::TInterval<ui64> shard, TInstant timestamp, const IStopCondition& stopper = IStopCondition::NonStop());
    std::tuple<TGroupedShardResources, bool> GetGroupedByTSResourcesForShard(NUtil::TInterval<ui64> shard, TDuration maxAge, const IStopCondition& stopper = IStopCondition::NonStop());
    std::tuple<TGroupedShardResources, bool> GetGroupedByTSResourcesForShard(NUtil::TInterval<ui64> shard, TInstant newerThan = TInstant::Zero(), TInstant olderThanOrEqualTo = TInstant::Max(), const IStopCondition& stopper = IStopCondition::NonStop());

protected:
    virtual void DoPublishSnapshot(const TSnapshot& snapshot) = 0;
    virtual TSnapshots DoGetSnapshots(const std::function<bool(TInstant)>&, const IStopCondition&) = 0;

protected:
    const TContext Context;

private:
    static const TShardResource* FindResourceInSnapshot(const TSnapshot& snapshot, NUtil::TInterval<ui64> shard);

private:
    using TSelf = IIndexSnapshotManager;
};
