#pragma once

#include <saas/library/hash_to_docid/doc_hash_iterator.h>
#include <saas/library/report_builder/abstract.h>
#include <saas/rtyserver/common/doc_search_info.h>
#include <saas/rtyserver/search/context/rty_search_context.h>

#include <util/generic/noncopyable.h>
#include <util/system/mutex.h>

namespace NMetaProtocol {
    class TDocument;
}

namespace NJson {
    class TJsonValue;
}

namespace NRTYFeatures {
    class TImportedFunctionsBuilder;
}

class ICustomReportBuilder;
class IIndexController;
class TCgiParameters;
class TParsedDocument;

namespace NRTYServer {

    class IIndexManagersStorage;

    class IDocSearchInfoIterator : public NSaas::IDocHashIterator {
    public:
        virtual TDocSearchInfo Get() const = 0;
        virtual bool IsDeleted() const = 0;

        std::pair<NSaas::TDocHash, ui32> GetHashWithId() const override;
    };

    class IIndexComponentManager: public TNonCopyable {
    private:
        bool Opened_;
        TMutex MutexOpened_;
    public:
        IIndexComponentManager(const TString& componentName)
            : ComponentName(componentName)
        {
            Opened_ = false;
        }

        virtual ~IIndexComponentManager() {
            CHECK_WITH_LOG(!Opened_);
        }

        virtual bool DoClose() = 0;
        virtual bool DoOpen() = 0;

        bool IsOpened() const {
            return Opened_;
        }

        bool Open();
        bool Close();

        virtual ui32 GetDocumentsCount() const = 0;
        virtual void GetExportedFunctions(NRTYFeatures::TImportedFunctionsBuilder&) const {}
        virtual void InitInteractions(const IIndexManagersStorage& /* storage */) = 0;
        virtual bool GetDocInfo(const ui32 /* docId */, NJson::TJsonValue& /* result */) const { return false; }
        virtual bool UpdateDoc(ui32 /*docId*/, const TAtomicSharedPtr<TParsedDocument> /*doc*/) {
            FAIL_LOG("Update not implemented");
        }
        virtual ui32 RemoveDocids(const TVector<ui32>& /*docids*/) {
            return 0;
        }

        virtual ui32 RemoveDocidsUnsafe(const TVector<ui32>& /*docids*/) {
            return 0;
        }

        virtual ui32 MarkDocIdsForDeleteUnsafe(const TVector<ui32>& /*docids*/, ui32 /*marker*/) {
            return 0;
        }

        virtual ERTYSearchResult DoSearch(const TRTYSearchRequestContext& /*context*/, ICustomReportBuilder& /*reportBuilder*/, const IIndexController& /*controller*/) const {
            return SR_NOT_FOUND;
        }

        virtual THolder<IDocSearchInfoIterator> GetDocSearchInfoIterator() const {
            return nullptr;
        }

        virtual void Prefetch() const {
        }

        ERTYSearchResult Search(const TRTYSearchRequestContext& context, ICustomReportBuilder& reportBuilder, const IIndexController& controller) const {
            return DoSearch(context, reportBuilder, controller);
        }

        const TString& GetComponentName() const {
            return ComponentName;
        }

    protected:
        const TString ComponentName;
    };

    class IRTYIndexManager: public IIndexComponentManager {
    public:
        IRTYIndexManager(const TString& componentName)
            : IIndexComponentManager(componentName)
        {

        }
        virtual i64 GroupAttrValue(ui32 docid, const char* attrname, i64 defaultValue) const = 0;
        virtual i64 PrnValue(ui32 docid, i64 defaultValue) const = 0;
        virtual ui32 GetSearchableDocumentsCount() const = 0;
        virtual bool GetIndexInfo(NJson::TJsonValue& result) const = 0;
    };

    struct TDirectManagerOps {
        static inline bool Acquire(IIndexComponentManager& manager) {
            return manager.Open();
        }
        static inline bool Release(IIndexComponentManager& manager) {
            return manager.Close();
        }
    };
    struct TInverseManagerOps {
        static inline bool Acquire(IIndexComponentManager& manager) {
            return manager.Close();
        }
        static inline bool Release(IIndexComponentManager& manager) {
            return manager.Open();
        }
    };

    template <class TOps, bool Strict>
    class TCommonManagerGuard {
    public:
        inline TCommonManagerGuard(IIndexComponentManager& manager)
            : Manager(manager)
        {
            bool acquired = TOps::Acquire(Manager);
            if (Strict) {
                CHECK_WITH_LOG(acquired);
            }
        }
        inline ~TCommonManagerGuard() {
            if (Strict || Manager.IsOpened()) {
                CHECK_WITH_LOG(TOps::Release(Manager));
            }
        }
    private:
        IIndexComponentManager& Manager;
    };

    using TManagerGuard = TCommonManagerGuard<TDirectManagerOps, true>;
    using TInverseManagerGuard = TCommonManagerGuard<TInverseManagerOps, true>;
    using TManagerTryGuard = TCommonManagerGuard<TDirectManagerOps, false>;

    template <class T>
    class TManagerHolder: public TAtomicSharedPtr<T> {
    public:
        using TBase = TAtomicSharedPtr<T>;
        using TBase::TBase;

        TManagerHolder(T* manager)
            : TBase(manager)
        {
            CHECK_WITH_LOG(manager);
            if (TBase::RefCount() == 1) {
                CHECK_WITH_LOG(TBase::Get()->Open());
            }
        }

        ~TManagerHolder() {
            if (TBase::RefCount() == 1) {
                CHECK_WITH_LOG(TBase::Get()->Close());
            }
        }
    };
}
