#pragma once

#include <robot/library/yt/static/table.h>
#include <saas/library/rtyt/lib/operation/factory.h>
#include <saas/rtyserver_jupi/library/rtyt/docid_mapper.h>

#include <contrib/libs/protobuf/src/google/protobuf/descriptor.h>

class TTableRegistry {
    TVector<TAtomicSharedPtr<NJupiter::TTableBase>> Tables;
    TVector<const ::google::protobuf::Descriptor*> Descriptors;
    TVector<TIntrusivePtr<TRTYTDocIdRemapperBase>> Mappers;
    THashMap<TString, ui32> IdByName;

    template<class T, class = void>
    struct THasDocId : std::false_type { };

    template<class T>
    struct THasDocId<T, std::void_t<decltype(&T::GetDocId)>> : std::true_type { };
public:
    
    template <typename TProto>
    void RegisterTable(const TFsPath& /*directory*/, const NJupiter::TTable<TProto>& table) {
        if constexpr (THasDocId<TProto>::value) {
            IdByName[table.GetName()] = Tables.size();
            Tables.emplace_back(new NJupiter::TTableBase(table));
            Descriptors.push_back(TProto::descriptor());
            Mappers.push_back(MakeIntrusive<TRTYTDocIdRemapper<TProto>>());
            REGISTER_RTYT_MAPPER(TRTYTDocIdRemapper<TProto>);
            INFO_LOG << "Registered table: " << table.GetName() << Endl;
        } else {
            INFO_LOG << "skip table: " << table.GetName() << ", has no DocId field" << Endl;
        }
    }

    NJupiter::TTableBase* GetTableByName(const TString& name) const {
        auto findResult = IdByName.find(name);
        Y_ENSURE(findResult != IdByName.end(),  yexception() << "[GetTableByName] No such table: '" << name << "' registered");
        return Tables[findResult->second].Get();
    }

    const ::google::protobuf::Descriptor* GetProtoByName(const TString& name) const {
        auto findResult = IdByName.find(name);
        Y_ENSURE(findResult != IdByName.end(),  yexception() << "[GetProtoByName] No such table: '" << name << "' registered");
        return Descriptors[findResult->second];
    }

    TIntrusivePtr<TRTYTDocIdRemapperBase> GetMapperByName(const TString& name) {
        auto findResult = IdByName.find(name);
        Y_ENSURE(findResult != IdByName.end(),  yexception() << "[GetMapperByName] No such table: '" << name << "' registered");
        return Mappers[findResult->second];
    }
};

