#pragma once

#include "time_cached.h"

#include <saas/library/storage/abstract.h>

#include <util/generic/hash.h>
#include <util/system/rwlock.h>
#include <util/datetime/base.h>

namespace NZooKeeper {
    class TZooKeeper;
}

namespace NSaas {

    class TZooStorage;

    class TZooLock: public TAbstractLock {
    private:
        class TCachedIsLocked: public ITimeCachedValue<bool> {
        public:
            TCachedIsLocked(const TZooLock& lock)
                : ITimeCachedValue(TDuration::Seconds(5))
                , Lock(lock)
            {
            }

            bool DoGet() override {
                return Lock.IsLockedImpl();
            }

        private:
            const TZooLock& Lock;
        };

        bool BuildLock();
        bool ReleaseLock();

    private:
        const TZooStorage& Storage;
        TString Id;
        bool WriteLock;
        TString LockName;
        mutable TCachedIsLocked IsLocked_;

    public:
        TZooLock(const TZooStorage& storage, const TString& id, bool writeLock);

        virtual ~TZooLock() override;

        bool IsLockTaken() const;
        virtual bool IsLocked() const override;

    private:
        bool IsLockedImpl() const;
    };

    class TZooStorage : public IVersionedStorage {
    private:
        typedef THashMap<TString, TString> TValueCache;
    private:
        const TString ZooAddresses;
        const TString Root;
        const ui32 ZooLogLevel;

        TRWMutex RWMutex;
        mutable THolder<NZooKeeper::TZooKeeper> ZK;
        mutable TValueCache ValueCache;

    private:
        bool BuildZK(const TString& reason) const;
        TAbstractLock::TPtr NativeLockNode(const TString& path, const bool isWrite, const TDuration timeout = TDuration::Seconds(100000)) const;

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

    public:
        TZooStorage(const TOptions& options);

        bool FastRemoveNode(const TString& key) const;
        void MkDirs(const TString& path) const;
        virtual bool GetVersion(const TString& key, i64& version) const override;
        virtual bool RemoveNode(const TString& key, bool withHistory = false) const override;
        virtual bool ExistsNode(const TString& key) const override;
        virtual bool GetValue(const TString& key, TString& result, i64 version = -1, bool lock = true) const override;
        virtual bool SetValue(const TString& key, const TString& value, bool storeHistory = true, bool lock = true, i64* version = nullptr) const override;
        virtual bool GetNodes(const TString& key, TVector<TString>& result, bool withDirs = false) const override;
        virtual bool CreatePersistentSequentialNode(const TString& key, const TString& data) const override;
        static IVersionedStorage::TFactory::TRegistrator<TZooStorage> Registrator;
        NZooKeeper::TZooKeeper* GetZK() const {
            return ZK.Get();
        }
    };

}
