#pragma once

#include "consistent_options.h"

#include <saas/library/indexer_client/options.h>
#include <saas/library/sharding/sharding.h>
#include <saas/library/tvm/tvm.h>
#include <saas/rtyserver/synchronizer/library/config/config.h>
#include <saas/rtyserver/synchronizer/library/sync.h>

#include <util/generic/string.h>
#include <util/generic/set.h>
#include <util/generic/size_literals.h>

namespace NFusion {

enum class EStreamType {
    OldDistributor /* OldDistributor */,
    Distributor /* Distributor */,
    MapReduce /* MapReduce */,
    PersQueue /* PersQueue */,
    Snapshot /* Snapshot */
};

struct TShardFilterOptions {
    ui64 ShardMin = 0;
    ui64 ShardMax = 0;
    NSaas::ShardingType ShardType = NSaas::UrlHash;
    ui32 KpsShift = 0;

    bool IsEnabled() const {
        return ShardMin != 0 || ShardMax != 0;
    }
};

struct TBaseStreamConfig {
    TString Name = "default";
    ui32 StreamId = 1;
    EStreamType StreamType = EStreamType::Distributor;

    ui64 ShardMin = 0;
    ui64 ShardMax = 0;
    ui32 Shard = 0;
    ui32 NumShards = 1;

    TShardFilterOptions ShardFilter;

    TDuration PauseOnMergeDuration = TDuration::Seconds(10);
    bool UseIndexTimestamp = true;
    bool AsyncStart = false;
    bool Enabled = true;
    bool PauseOnMerge = false;
    TString YTHosts = TString();
};

enum class ERealtimeOverrideMode {
    DoNotOverride,
    ForceFalse,
    ForceTrue
};

struct TDocStreamConfig : public TBaseStreamConfig {
    bool ReopenIndexesOnExhaustion = true;

    ui64 MaxAgeToGetSec = Max<i64>();       // Oldest age to get
    ui64 MaxDocAgeToKeepSec = Max<i64>();   // Oldest doc age to keep in the index
    bool ReceiveDurationAsDocAge = false;      // Take document age as (Now - message.GetReceiveTimesamp())
    ui64 MemIndexAgeSec = Max<i32>();       // Do not send docs older than this to mem index
    ui64 MemoryIndexDistAgeThreshold = Max<i64>(); // Maximum age for documents to get to memory index
    ui64 SearchOpenThreshold = Max<i64>();  // Condition for opening
    TDuration TimeToSleep = TDuration::Seconds(1);  // Default time to sleep when a document is not received
    TDuration MaxIdleTime = TDuration::Seconds(100);

    // These settings are used when search gets closed by the "ban_search" command. Whenever it happens, |SearchOpenThresholdPessimistic|
    // is used instead of |SearchOpenThreshold| during next |SearchOpenPessimizationDelaySeconds| seconds
    ui64 SearchOpenThresholdPessimistic = 0;
    ui64 SearchOpenPessimizationDelaySeconds = TDuration::Minutes(30).Seconds();

    ERealtimeOverrideMode RealtimeOverride = ERealtimeOverrideMode::DoNotOverride;

    TSet<TString> DistributionMetrics; //DistributionAttributes of incoming messages to track

    TIndexingClientOptions ClientOptions;
    TString IndexingHost;
    ui16 IndexingPort = 0;
    bool FastIndexWhenNoSearch = false;
};

struct TSyncOptions {
    TDuration SyncThreshold = TDuration::Max();
    TDuration SyncAttemptPause = TDuration::Seconds(100);
    ui32 SyncMaxAttempts = 3;

    TString SnapshotManager = "zookeeper";
    TString SyncServer;
    TString SyncPath;
    bool EnableSearchWhileFetching = false;

    TMaybe<NRTYServer::TResourceFetchConfig> ResourceFetchConfig;
};

enum class EDataSourceType {
    ExternalAPI,
    Zookeeper
};

struct TZookeeperDataSourceConfig {
    TString ZKPath;
    TString ZKServers;
    ui32 ZKRequestsMaxAttempts = 3;
    TDuration ZKLostConnectionWaitTime = TDuration::Seconds(30);
    TDuration ZKMaxValidDataDelay = TDuration::Minutes(30);
};

struct TExternalAPIDataSourceConfig {
    TString Host;
    ui32 Port = 80;
    TString Query;
    TDuration Timeout = TDuration::Seconds(10);
};

struct TDatacenterCheckerConfig {
    bool Enabled = true;
    EStreamType StreamType;

    EDataSourceType DataSourceType = EDataSourceType::ExternalAPI;
    std::variant<TExternalAPIDataSourceConfig, TZookeeperDataSourceConfig> DataSource;

    TDuration CheckInterval = TDuration::Minutes(5);
    TDuration BlockDuration = TDuration::Minutes(30);

    ui32 MaxConsecutiveErrors = 5;
};

enum class ESnapshotStreamMode {
    OnRequest /* OnRequest */,
    OnSchedule /* OnSchedule */
};

struct TSnapshotStreamConfig
    : public TBaseStreamConfig
    , public TSyncOptions {
    ESnapshotStreamMode Mode = ESnapshotStreamMode::OnRequest;

    TDuration IterationsPause = TDuration::Minutes(5);
    TDuration FetchRetryPause = TDuration::Seconds(100);
    TDuration PauseAfterPartialConsume = TDuration::Minutes(1);
    ui32 FetchMaxAttempts = 3;
    ui32 MaxResourcesPerIteration = 1;

    TString SnapshotManager;
    TString SnapshotServer;
    TString SnapshotPath;
    TString SnapshotToken;
    bool UseGroups = false;

    TMaybe<NRTYServer::TResourceFetchConfig> ResourceFetchConfig;

    TInstant FixedTimestamp = TInstant::Zero();
    NRTYServer::EConsumeMode ConsumeMode = NRTYServer::EConsumeMode::Replace;

    TInstant AllowEmptyIndexUntil;
};

struct TMapReduceStreamConfig
    : public TDocStreamConfig
    , public TSyncOptions
{
    TString Server;
    TString Token;
    TString Table;
    TString BackupTable;
    TString RowProcessor = "message";
    ui32 MasterTableCheckInterval = 60;
    ui32 BackupSizeThresholdPercent = 0;  // off by default; set to X to apply backup when "normal" stream is longer by X%
};

struct TPersQueueStreamConfig
    : public TDocStreamConfig
    , public TSyncOptions
{
    using TReplicaIds = std::pair<TString, TString>;
    using TReplicaIdsList = TVector<TReplicaIds>;

    bool UseShardedLogtype = false;
    TString Server;
    TString Ident;
    TString TopicName;
    ui32 NumTopics = 1;
    TString UserPrefix;
    TString Consumer;
    ui32 BatchSize = 10000; // SAAS-6354
    ui32 BatchInBytes = 100_MB; // SAAS-6354
    ui32 Timeout = 30000;
    ui32 ReadTimeout = 10000;
    ui32 LagTimeout = 100000;
    ui32 SleepTimeout = 1000;
    TDuration StartBlockTime = TDuration::Minutes(1);
    TDuration ReceiveDelay = TDuration::Seconds(5);
    ui32 QueueSize = 2500;
    bool UseIpv6 = true;
    TString Replica;
    bool StreamRead = true;
    bool UseMirroredPartitions = true;
    bool UseNewProtocol = false; // deprecated? see SAAS-6359
    bool UseNewPQLib = false;
    bool UseDirectoryTopicFormat = false;
    TString Datacenters;
    i64 OverlapAge = 10;
    ui32 PQLibThreads = 3;
    TReplicaIdsList ReplicaIdsList;
    TString LockServers;
    TString LockPath;
    TMaybe<NSaas::TTvmConfig> TvmConfig;
};

struct TStreamConfig
    : public TDocStreamConfig
    , public TSyncOptions
{
    NRealTime::TDistributors DistributorServers;
    NRealTime::TDistributors PreferredDistributor;
    bool   UseCompression = false;
    bool   ConsistentClient = false;

    TString DistributorStream;
    TString DistributorAttributes;

    i64 OverlapAge = 10;            // When restarted, we take docs of age (now - LastAckTime + OverlapAge)

    TString ClientId;
    TConsistentClientReplicas DistributorReplicas;
    TConsistentClientOptions ConsistentClientOptions;

    float MaxUnresolvedReplicas = 0.2f;
    float WarnUnresolvedReplicas = 0.1f;
};

}
