#include "repair.h"
#include "file_indexer.h"

#include <saas/rtyserver/components/fullarchive/globals.h>
#include <saas/rtyserver/components/fullarchive/name_normalizer.h>
#include <saas/rtyserver/common/message_collect_server_info.h>
#include <saas/rtyserver/config/repair_config.h>
#include <saas/util/logging/exception_process.h>

#include <kernel/multipart_archive/multipart.h>

#include <library/cpp/balloc/optional/operators.h>

TRepairerTask::TRepairerTask(TString taskDir, TIndexRepair& owner)
    : TaskDir(taskDir)
    , Owner(owner)
{
    Owner.OnCreateTask(this);
}

bool TRepairerTask::FixByLog(const TString& tempDir) {
    INFO_LOG << "Start FixByLog for " << tempDir << Endl;
    bool result = false;
    TRY
        NRTYServer::NFullArchive::NormalizeNames(tempDir);
        if (!TDiskFAManager::ExistsLayer(tempDir, NRTYServer::NFullArchive::FullLayer)) {
            INFO_LOG << "No logFile " << tempDir << " found. Deleting " << tempDir << Endl;
            RemoveDirWithContents(tempDir);
            return true;
        }
        if (TDiskFAManager::LayerIsEmpty(tempDir, NRTYServer::NFullArchive::FullLayer)) {
            INFO_LOG << "Log file " << tempDir << " is empty. Deleting " << tempDir << Endl;
            return true;
        }

        TFileIndexer indexer(tempDir, Owner, Owner.GetModifier());
        result = indexer.Run();
    CATCH("while reparing " + tempDir)
    INFO_LOG << "FixByLog finished for " << tempDir << Endl;
    return result;
}

void TRepairerTask::Process(void* /*ThreadSpecificResource*/){
    ThreadDisableBalloc();
    THolder<TRepairerTask> This(this);

    RTY_MEM_LOG("Repair task for " + TaskDir + " start");
    NOTICE_LOG << "Repairing " << TaskDir << Endl;
    bool repairResult = false;
    if (strstr(TFsPath(TaskDir).Basename().data(), "temp_")) {
        INFO_LOG << "FixByLog started" << Endl;
        repairResult = FixByLog(TaskDir);
        INFO_LOG << "FixBylog finished" << Endl;
    }
    NOTICE_LOG << "Repairing " << TaskDir << " finished" << Endl;
    if (repairResult && NFs::Exists(TaskDir))
        RemoveDirWithContents(TaskDir);
    else
        ERROR_LOG << "errors while repair " << TaskDir << " repairResult =" << repairResult << Endl;
    Owner.OnEndTask(this);
    RTY_MEM_LOG("Repair task for " + TaskDir + " finished");
}

bool TIndexRepair::Process(IMessage* message) {
    TMessageCollectServerInfo* messInfo = dynamic_cast<TMessageCollectServerInfo*>(message);
    if (messInfo) {
        TGuard<TMutex> g(Mutex);
        messInfo->AddRepairerTasks(TasksExecuted);
        return true;
    }
    TMessageSendRepairInfo* messRepair = dynamic_cast<TMessageSendRepairInfo*>(message);
    if (messRepair) {
        TGuard<TMutex> g(Mutex);
        bool result = true;
        auto task = MakeHolder<TRepairerTask>(messRepair->GetTempDir(), *this);
        if (IsStarted) {
            result = RepairTasks.Add(task.Get());
            if (result) {
                Y_UNUSED(task.Release());
            }
        } else {
            TasksBuffer.push_back(std::move(task));
        }
        if (!result) {
            WARNING_LOG << "Task for repairer " << messRepair->GetTempDir() << " wasn't added in queue" << Endl;
        }
        return true;
    }
    return false;
}

bool TIndexRepair::Start() {
    if (!Config.GetRepairConfig().Enabled) {
        return false;
    }
    TGuard<TMutex> g(Mutex);
    IsStarted = true;
    RepairTasks.Start(Config.GetRepairConfig().Threads);
    for (auto& task: TasksBuffer) {
        if (!RepairTasks.Add(task.Get())) {
            WARNING_LOG << "Task for repairer " << task->GetTaskDir() << "wasn't added in queue" << Endl;
        } else {
            Y_UNUSED(task.Release());
        }
    }
    TasksBuffer.clear();
    return true;
}
