#include "slot_info_processor.h"

#include "synchronizer.h"
#include "config.h"

#include <saas/rtyserver/common/common_messages.h>
#include <saas/rtyserver/common/message_collect_server_info.h>
#include <saas/rtyserver/common/search_control.h>
#include <saas/rtyserver/config/realm_config.h>
#include <saas/library/sharding/sharding.h>

#include <library/cpp/digest/md5/md5.h>

#include <util/stream/file.h>

namespace NRTYServer {

    TSlotInfoProcessor::TSlotInfoProcessor(TSynchronizer* synchronizer)
        : ISynchronizerProcessor(synchronizer)
        , SlotInfoFile(TFsPath(Synchronizer->GetRTYConfig().GetDaemonConfig().GetController().ConfigsRoot) / Synchronizer->GetConfig()->SlotInfoFile)
        , PersistentSearchBanFile(TFsPath(Synchronizer->GetRTYConfig().GetRealmListConfig().GetMainRealmConfig().RealmDirectory) / Synchronizer->GetConfig()->PersistentSearchBanFile)
        , SearchDisabledBySlotInfo(false)
        , SearchDisabledByPersistentBan(false)
    {
    }

    bool TSlotInfoProcessor::ProcessRequest(const TCgiParameters& cgi, TStringBuf postBuffer, TAsyncTaskExecutor::TTask& task) {
        Y_UNUSED(task);
        DEBUG_LOG << "Start slot_info request processing " << cgi.Print() << Endl;

        const TString& subAction = cgi.Get("subaction");
        Y_ENSURE(!!subAction, "TSlotInfoProcessor: there is no subaction");

        if (subAction == "set") {
            Set(postBuffer);
            Update();
        } else if (subAction == "create_persistent_search_ban") {
            CreatePersistentSearchBan();
            Update();
        } else if (subAction == "remove_persistent_search_ban") {
            RemovePersistentSearchBan();
            Update();
        } else if (subAction == "check_persistent_search_ban") {
            task.GetReply()->InsertValue("exists", SearchDisabledByPersistentBan);
        } else if (subAction == "reread") {
            Update();
        } else {
            ythrow yexception() << "TSlotInfoProcessor: invalid subaction: " << subAction.Quote();
        }

        DEBUG_LOG << "Finish slot_info request processing " << cgi.Print() << Endl;
        return true;
    }

    void TSlotInfoProcessor::Start() {
        if (Synchronizer->GetConfig()->PersistentSearchBanOnEmptyIndex) {
            CheckEmptyIndex();
        }

        Update();

        if (Synchronizer->GetConfig()->EnableSearchOnStart) {
            if (!SearchDisabledBySlotInfo && !SearchDisabledByPersistentBan) {
                INFO_LOG << "Enabling search on synchronizer start..." << Endl;
                NRTYServer::EnableSearch();
                INFO_LOG << "Enabling search on synchronizer start... done" << Endl;
            }
        }
    }

    bool TSlotInfoProcessor::CollectServerInfo(TMessageCollectServerInfo* messInfo) {
        messInfo->SetPersistentSearchBan(SearchDisabledByPersistentBan);
        messInfo->SetSlotInfoSearchBan(SearchDisabledBySlotInfo);
        return true;
    }

    void TSlotInfoProcessor::Set(TStringBuf contents) {
        constexpr ui32 md5Size = 32;
        char md5[md5Size + 1];
        MD5::Data(contents.data(), contents.size(), md5);

        TFileHashInfo fileInfo;
        fileInfo.Hash = TStringBuf(md5, md5Size);
        fileInfo.Size = contents.size();

        {
            TGuard<TMutex> g(SlotInfoMutex);
            TUnbufferedFileOutput out(SlotInfoFile);
            out << contents;
            SendGlobalMessage<TUpdateConfigHash>(SlotInfoFile, fileInfo);
        }
    }

    void TSlotInfoProcessor::CreatePersistentSearchBan() {
        TGuard<TMutex> g(SlotInfoMutex);
        INFO_LOG << "Creating persistent search ban: " << PersistentSearchBanFile.GetPath().Quote() << Endl;
        PersistentSearchBanFile.Touch();
    }

    void TSlotInfoProcessor::RemovePersistentSearchBan() {
        TGuard<TMutex> g(SlotInfoMutex);
        INFO_LOG << "Removing persistent search ban: " << PersistentSearchBanFile.GetPath().Quote() << Endl;
        PersistentSearchBanFile.DeleteIfExists();
    }

    bool TSlotInfoProcessor::CheckPersistentSearchBan() {
        TGuard<TMutex> g(SlotInfoMutex);
        INFO_LOG << "Checking persistent search ban: " << PersistentSearchBanFile.GetPath().Quote() << Endl;
        return PersistentSearchBanFile.Exists();
    }

    void TSlotInfoProcessor::CheckEmptyIndex() {

        TGuard<TMutex> g(SlotInfoMutex);

        INFO_LOG << "Checking if index is empty..." << Endl;
        TMessageCollectServerInfo info;
        if (!SendGlobalMessage(info) || info.GetSearchableDocs() == 0) {
            INFO_LOG << "Checking if index is empty... yes, creating persistent search ban" << Endl;
            PersistentSearchBanFile.Touch();
        } {
            INFO_LOG << "Checking if index is empty... no" << Endl;
        }
    }

    void TSlotInfoProcessor::Update() {
        TGuard<TMutex> g(SlotInfoMutex);

        INFO_LOG << "Checking persistent search ban..." << Endl;
        if (PersistentSearchBanFile.Exists()) {
            INFO_LOG << "Checking persistent search ban... exists, disabling search..." << Endl;
            SearchDisabledByPersistentBan = true;
            NRTYServer::DisableSearch();
            INFO_LOG << "Checking persistent search ban... exists, disabling search... OK" << Endl;
        } else {
            if (SearchDisabledByPersistentBan && !SearchDisabledBySlotInfo) {
                INFO_LOG << "Checking persistent search ban... none, enabling search..." << Endl;
                NRTYServer::EnableSearch();
                INFO_LOG << "Checking persistent search ban... none, enabling search... OK" << Endl;
            } else {
                INFO_LOG << "Checking persistent search ban... none, don't modify search status" << Endl;
            }
            SearchDisabledByPersistentBan = false;
        }

        if (!SlotInfoFile.Exists()) {
            INFO_LOG << "slot info file not found: " << SlotInfoFile.GetPath().Quote() << Endl;
            return;
        }

        TIFStream input(SlotInfoFile);
        NSaas::TSlotInfo slotInfo;
        if (!slotInfo.FromString(input.ReadAll())) {
            ERROR_LOG << "slot info file is corrupted" << Endl;
            return;
        }

        {
            INFO_LOG << "Checking indexing ban in slot info..." << Endl;
            const auto action = slotInfo.DisableIndexing ? TMessageIndexingControl::Disable : TMessageIndexingControl::Enable;
            INFO_LOG << "Checking indexing ban in slot info... " << (slotInfo.DisableIndexing ? "disabling" : "enabling") << " indexing" << Endl;
            const auto& message = SendGlobalMessage<TMessageIndexingControl>(action);
            VERIFY_WITH_LOG(slotInfo.DisableIndexing ^ message.IsRunning(), "indexing control failed");
            INFO_LOG << "Checking indexing ban in slot info... done" << Endl;
        }
        {
            INFO_LOG << "Checking search ban in slot info..." << Endl;
            if (slotInfo.DisableSearch) {
                INFO_LOG << "Checking search ban in slot info... search ban exists, disabling search..." << Endl;
                NRTYServer::DisableSearch();
                SearchDisabledBySlotInfo = true;
                INFO_LOG << "Checking search ban in slot info... search ban exists, disabling search... OK" << Endl;
            } else {
                if (SearchDisabledBySlotInfo && !SearchDisabledByPersistentBan) {
                    //FIXME(salmin) may enable search which is still banned by docfetcher
                    INFO_LOG << "Checking search ban in slot info... search ban revoked, enabling search..." << Endl;
                    NRTYServer::EnableSearch();
                    INFO_LOG << "Checking search ban in slot info... search ban revoked, enabling search... OK" << Endl;
                } else {
                    INFO_LOG << "Checking search ban in slot info... none, don't modify search status" << Endl;
                }
                SearchDisabledBySlotInfo = false;
            }
        }

    }
}
