#include "component.h"

#include "builder.h"
#include "manager.h"
#include "parsed_entity.h"

#include <dj/lib/shard2/index/shard_resource.h>

#include <saas/rtyserver/config/config.h>
#include <saas/rtyserver/config/common_indexers_config.h>
#include <saas/rtyserver/config/indexer_config.h>
#include <saas/rtyserver/config/merger_config.h>
#include <saas/rtyserver/config/realm_config.h>
#include <saas/rtyserver/config/searcher_config.h>
#include <saas/rtyserver/indexer_core/index_dir.h>
#include <saas/rtyserver/indexer_core/merger_interfaces.h>

#include <search/config/virthost.h>

#include <library/cpp/logger/global/global.h>
#include <library/cpp/protobuf/util/pb_io.h>

#include <util/folder/path.h>

namespace NRTYServer {
    TDjComponent::TDjComponent(const TRTYServerConfig& config)
        : IIndexComponent(config.ComponentsSet.contains(DjComponentName))
        , Config(&config)
    {
        const auto userDirectives = config
            .GetSearcherConfig()
            .DefaultConfigInstances[NRTYServer::TSearcherConfig::sctBase]
            ->UserDirectives;
        const auto configPath = userDirectives.at("ShardBuilderConfig");
        TryToRepair = userDirectives.contains("TryToRepair") && IsTrue(userDirectives.Get("TryToRepair"));

        Y_ENSURE(
                config.GetMergerConfig().DocIdGenerator ==
                NRTYServer::TMergerConfig::EDocIdGenerator::Sequential);
        ShardBuilderConfig = ParseFromTextFormat<NDJ::TShardBuilderConfig>(configPath);

        if (userDirectives.contains("ShardBuilderMaxThreadCount")) {
            ShardBuilderConfig.SetMaxThreadCount(FromString(userDirectives.Get("ShardBuilderMaxThreadCount")));
        }

        if (userDirectives.contains("KeepPrepFiles")) {
            ShardBuilderConfig.SetKeepPrepFiles(FromString<bool>(userDirectives.Get("KeepPrepFiles")));
        }

        SearchIndexBuilder.ConstructInPlace(ShardBuilderConfig);

        Merger.ConstructInPlace(SearchIndexBuilder.Get());
        Y_ENSURE(!config.GetCommonIndexers().UseSlowUpdate);
    }

    THolder<IIndexComponentBuilder> TDjComponent::CreateBuilder(
            const TBuilderConstructionContext& context) const {
        return MakeHolder<TDjBuilder>(
                    context.TempDir.PathName(),
                    Config->GetRealmListConfig().GetRealmConfigByConfigName(context.RealmName).GetIndexerConfigDisk().PreparatesMode ?
                        nullptr : SearchIndexBuilder.Get(),
                    SearchIndexBuilder && ShardBuilderConfig.GetKeepPrepFiles());
    }

    THolder<IIndexComponentManager> TDjComponent::CreateManager(
            const TManagerConstructionContext& context) const {
        switch(context.IndexType) {
            case IIndexController::MEMORY:
                return nullptr;
            case IIndexController::FINAL:
            case IIndexController::DISK:
            case IIndexController::PREPARED:
                return MakeHolder<TDjComponentManager>();
            default:
                FAIL_LOG("Incorrect index type");
        }
    }

    IComponentParser::TPtr TDjComponent::BuildParser() const {
        return new TDjDocParser{};
    }

    IParsedEntity::TPtr TDjComponent::BuildParsedEntity(IParsedEntity::TConstructParams& params) const {
        return new TDjParsedEntity(params);
    }

    bool TDjComponent::DoMerge(const TMergeContext& context) const {
        Y_ENSURE(Merger);
        Merger->Merge(context);
        return true;
    }

    void TDjComponent::CheckAndFix(const TNormalizerContext& context) const {
        if (!DoAllRight(context)) {
            INFO_LOG << "Going to rebuild the index\n";
            SearchIndexBuilder->Build(context.Dir.PathName(), context.Dir.PathName());
        } else {
            INFO_LOG << "Index is OK\n";
        }
    }

    bool TDjComponent::DoAllRight(const TNormalizerContext& context) const {
        if (!HasIndexDirPrefix(context.Dir.PathName(), DIRPREFIX_INDEX)) {
            return true;
        }
        if (!TryToRepair) {
            return true;
        }
        NDJ::NShard::TShardResource shard{
            JoinFsPaths(context.Dir.PathName(), "shard.bin"), /* lockMemory = */ false};
        return !shard.BuilderConfig() || shard.BuilderConfig()->SerializeAsString() ==
            ShardBuilderConfig.SerializeAsString();
    }
}
