#pragma once
#include <saas/library/daemon_base/module/module.h>
#include <saas/library/storage/zoo/zoo_storage.h>
#include <library/cpp/mediator/messenger.h>

#include <library/cpp/watchdog/lib/interface.h>

namespace NRTYServer {
    const TString ZooSynchronizerModuleName = "ZooSynchronizer";

    const ui32 MinimumVersion = 1;
    const ui32 UndefinedVersion = 0;

    class ISwitcher {
    public:
        class ILock {
        public:
            virtual ~ILock() {}
        };
        using ILockPtr = THolder<ILock>;
    public:
        virtual ~ISwitcher() {}

        virtual ILockPtr Lock() = 0;
    };
    using ISwitcherPtr = TAtomicSharedPtr<ISwitcher>;
}

class TZooSynchronizerConfig: public TPluginConfig<IDaemonModuleConfig> {
private:
    ui32 CheckIntervalSeconds = 30;
    TString ServiceName;
    TString CType;
    TString Shard;
public:
    NSaas::TStorageOptions StorageOptions;

private:
    static TFactory::TRegistrator<TZooSynchronizerConfig> Registrator;

public:
    ui32 GetCheckIntervalSeconds() const {
        return CheckIntervalSeconds;
    };

    const TString& GetServiceName() const {
        return ServiceName;
    }

    const TString& GetShard() const {
        return Shard;
    }

    const TString& GetCType() const {
        return CType;
    }

protected:
    virtual bool DoCheck() const override {
        return true;
    }

    virtual void DoInit(const TYandexConfig::Section& componentSection) override {
        StorageOptions.ZooOptions.Address = componentSection.GetDirectives().Value<TString>("Address", StorageOptions.ZooOptions.Address);
        StorageOptions.ZooOptions.Root = componentSection.GetDirectives().Value<TString>("Root", StorageOptions.ZooOptions.Root);
        CheckIntervalSeconds = componentSection.GetDirectives().Value<ui32>("CheckIntervalSeconds", CheckIntervalSeconds);
        ServiceName = componentSection.GetDirectives().Value<TString>("ServiceName", ServiceName);
        CType = componentSection.GetDirectives().Value<TString>("CType", CType);
        Shard = componentSection.GetDirectives().Value<TString>("Shard", Shard);
    }

    virtual void DoToString(IOutputStream& so) const override {
        so << "Address: " << StorageOptions.ZooOptions.Address << Endl;
        so << "Root: " << StorageOptions.ZooOptions.Root << Endl;
        so << "CheckIntervalSeconds: " << CheckIntervalSeconds << Endl;
        so << "ServiceName: " << ServiceName << Endl;
        so << "CType: " << CType << Endl;
        so << "Shard: " << Shard << Endl;
    }
};

class TZooSynchronizer: public IDaemonModule, public IMessageProcessor {
private:
    const IServerConfig& Config;
    const TZooSynchronizerConfig* SyncConfig;
    THolder<NSaas::IVersionedStorage> Storage;
    THolder<IWatchDog> WatchDogHandle;

private:
    static TDaemonModules::TFactory::TRegistrator<TZooSynchronizer> Registrator;

public:
    TZooSynchronizer(const IServerConfig& config)
        : Config(config)
        , SyncConfig(config.GetModuleConfig<TZooSynchronizerConfig>(NRTYServer::ZooSynchronizerModuleName))
    {
        Y_UNUSED(Config);
        VERIFY_WITH_LOG(SyncConfig, "Module configuration is absent");
    }

    virtual ~TZooSynchronizer() override {}

    virtual bool Process(IMessage* message) override;

    const TZooSynchronizerConfig& GetConfig() const {
        return *SyncConfig;
    }

    bool GetGlobalBaseVersion(ui32& result) const {
        CHECK_WITH_LOG(!!Storage);
        const TString pathVersionNode = "rty/" + SyncConfig->GetCType() + "/" + SyncConfig->GetServiceName() + "/base_version";
        TString versionNodeInfo;
        if (!Storage->ExistsNode(pathVersionNode) || !Storage->GetValue(pathVersionNode, versionNodeInfo))
            return false;
        return TryFromString<ui32>(versionNodeInfo, result);
    }

    virtual bool Start(const TStartContext& startContext) override;
    virtual bool Stop(const TStopContext& stopContext) override;

    virtual TString Name() const override {
        return NRTYServer::ZooSynchronizerModuleName;
    }
};

struct TRequestCurrentDatabaseVersion: public IMessage {
    ui32 Version = NRTYServer::UndefinedVersion;
};

class TMessageBaseVersionChanged: public IMessage {
public:
    const ui32 OldVersion;
    const ui32 NewVersion;
    const NRTYServer::ISwitcherPtr Switcher;

public:
    TMessageBaseVersionChanged(ui32 oldVersion, ui32 newVersion, NRTYServer::ISwitcherPtr switcher)
        : OldVersion(oldVersion)
        , NewVersion(newVersion)
        , Switcher(switcher)
    {
    }
};
