#pragma once

#include "config.h"

#include <library/cpp/object_factory/object_factory.h>
#include <library/cpp/logger/global/global.h>

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

namespace google {
    namespace protobuf {
        class Message;
    }
}

namespace NSaas {

    class TAbstractLock {
    public:
        using TPtr = TAtomicSharedPtr<TAbstractLock>;

    public:
        virtual ~TAbstractLock() {}
        virtual bool IsLocked() const = 0;
    };

    class IVersionedStorage {
    public:
        using TOptions = TStorageOptions;
        using TFactory = NObjectFactory::TParametrizedObjectFactory<IVersionedStorage, TOptions::TStorageType, TOptions>;

    public:
        static IVersionedStorage* Create(const TOptions& options);

    public:
        IVersionedStorage(const TOptions& options);
        virtual ~IVersionedStorage() {}

        virtual bool RemoveNode(const TString& key, bool withHistory = false) const = 0;
        virtual bool ExistsNode(const TString& key) const = 0;
        virtual bool GetVersion(const TString& key, i64& version) const = 0;
        virtual bool GetNodes(const TString& key, TVector<TString>& result, bool withDirs = false) const = 0;
        virtual bool GetValue(const TString& key, TString& result, i64 version = -1, bool lock = true) const = 0;
        virtual bool SetValue(const TString& key, const TString& value, bool storeHistory = true, bool lock = true, i64* version = nullptr) const = 0;
        virtual bool CreatePersistentSequentialNode(const TString& key, const TString& data) const = 0;

        void CopyPath(const TFsPath& oldRoot, const TString& newName, const TString& path) const;
        void GetValue(const TString& key, ::google::protobuf::Message& result, i64 version = -1, bool lock = true) const;
        void SetValue(const TString& key, const ::google::protobuf::Message& value, bool storeHistory = true, bool lock = true) const;
        virtual TAbstractLock::TPtr WriteLockNode(const TString& path, TDuration timeout = TDuration::Seconds(100000)) const;
        virtual TAbstractLock::TPtr ReadLockNode(const TString& path, TDuration timeout = TDuration::Seconds(100000)) const;

        const TOptions& GetOptions() const {
            return Options;
        }

    protected:
        virtual TAbstractLock::TPtr NativeWriteLockNode(const TString& path, TDuration timeout = TDuration::Seconds(100000)) const = 0;
        virtual TAbstractLock::TPtr NativeReadLockNode(const TString& path, TDuration timeout = TDuration::Seconds(100000)) const = 0;

    protected:
        const TOptions Options;
    };

};
