#include "executor.h"
#include "messages.h"
#include <saas/deploy_manager/scripts/cluster/script_controller.h>

namespace NRTYDeployServer {
    void TExecutor::NotifyMessage(NRTYDeploy::TClusterTask::TPtr task, const TString& subject, bool notInfo) {
        TString notif;
        if (!notInfo) {
            notif = task->GetNotificationInfo();
            INFO_LOG << "NOTIFICATION: subj:" << subject << ", info: " << notif << Endl;
        }
        if (!!Config.GetNotifyAddress() && !!task) {
            if (!notInfo) {
                NRTY::TMailInfoMessage mess(Config.GetNotifyAddress(), "", notif, subject);
                SendGlobalMessage(mess);
            } else {
                NRTY::TMailInfoMessage mess(Config.GetNotifyAddress(), "", task->GetNotificationShortInfo(), subject);
                SendGlobalMessage(mess);
            }
        }
    }

    void TExecutor::Process(void* ThreadSpecificResource) {
        while (!Active.WaitT(TDuration::Zero())) {
            try {
                NRTYDeploy::TDequeueInfo info;
                if (CommonData->GetQueue().Dequeue("deploy", info)) {
                    NRTYDeploy::TClusterTask::TPtr task = new NRTYDeploy::TClusterTask(info.Id, CommonData, Config.GetExecuteScriptThreads());
                    if (task->GetCurrentStatus() == NRTYDeploy::TClusterTask::ctsLoaded && (task->GetResultStatus() == NRTYDeploy::TClusterTask::rsSaved || task->GetResultStatus() == NRTYDeploy::TClusterTask::rsEnqueued || task->GetResultStatus() == NRTYDeploy::TClusterTask::rsInProgress)) {
                        task->InitExecutorHost();
                        TString id_mail = task->GetNotificationShortInfo();
                        NotifyMessage(task, "START CLUSTER TASK: " + id_mail, false);
                        SendGlobalDebugMessage<TMessageBeforeDMProcessTask>(info.Id);
                        task->Process(ThreadSpecificResource);
                        NRTYDeploy::TScriptController controller(CommonData->GetStorage(), info.Id);
                        if (controller.IsExecutable()) {
                            NotifyMessage(task, "FINISHED CLUSTER TASK: " + id_mail, false);
                        } else {
                            NotifyMessage(task, "SUSPENDED CLUSTER TASK: " + id_mail, false);
                        }
                    } else {
                        NotifyMessage(task, "CAN'T LOAD TASK: " + task->GetNotificationShortInfo() + " " + task->GetStatusInfo(), true);
                    }
                    CommonData->GetQueue().Ack(info);
                } else {
                    if (Active.WaitT(TDuration::Seconds(5))) {
                        break;
                    }
                }
            } catch (...) {
                ERROR_LOG << "Error while TExecutor::Process: " << CurrentExceptionMessage() << Endl;
            }
        }
    }

    void TExecutorRegular::Process(void* /*ThreadSpecificResource*/) {
        while (!Active.WaitT(TDuration::Seconds(1))) {
            try {
                for (auto&& i : Config.GetRegularCommands()) {
                    NController::TInfoRegularTask infoRegularTask;
                    const TString taskPath = "/regular_tasks/" + i.GetName();
                    try {
                        CommonData->GetStorage().GetValue(taskPath, infoRegularTask);
                    } catch (...) {
                        CommonData->GetStorage().RemoveNode(taskPath);
                    }
                    TDuration delta = Now() - TInstant::MicroSeconds(infoRegularTask.GetLastStart());

                    if (i.GetRestartInterval() < delta) {
                        auto lock = CommonData->GetStorage().WriteLockNode("/regular_tasks_locks/" + i.GetName(), TDuration::Seconds(2));
                        try {
                            if (CommonData->GetStorage().ExistsNode(taskPath))
                                CommonData->GetStorage().GetValue(taskPath, infoRegularTask);
                        } catch (...) {
                            continue;
                        }
                        if (!!lock && (Now() - TInstant::MicroSeconds(infoRegularTask.GetLastStart())) > i.GetRestartInterval()) {
                            NDaemonController::TAction::TPtr action = i.GetAction();
                            NDaemonController::TControllerAgent agent(Config.GetDeployManagerBalanser().Host, Config.GetDeployManagerBalanser().Port, nullptr, Config.GetDeployManagerBalanser().UriPrefix + "/");
                            INFO_LOG << "Action " << i.GetName() << " starting..." << Endl;
                            bool actionSuccess = agent.ExecuteAction(*action);
                            INFO_LOG << "Action " << i.GetName() << " finished, success=" << actionSuccess << Endl;
                            infoRegularTask.SetLastStart(Now().GetValue());
                            if (actionSuccess) {
                                infoRegularTask.SetLastFailsCnt(0);
                            } else {
                                ui32 lastFailsCnt = infoRegularTask.HasLastFailsCnt() ? infoRegularTask.GetLastFailsCnt() : 0;
                                infoRegularTask.SetLastFailsCnt(lastFailsCnt + 1);
                            }
                            CommonData->GetStorage().SetValue(taskPath, infoRegularTask, false);
                        }
                    }
                }
                ui32 minDelta = 5;
                for (auto&& i : Config.GetRegularCommands()) {
                    NController::TInfoRegularTask infoRegularTask;
                    const TString taskPath = "/regular_tasks/" + i.GetName();
                    try {
                        CommonData->GetStorage().GetValue(taskPath, infoRegularTask);
                    } catch (...) {
                        minDelta = Min<ui32>(minDelta, i.GetRestartInterval().Seconds());
                        CommonData->GetStorage().RemoveNode(taskPath);
                    }
                    TDuration delta = Now() - TInstant::MicroSeconds(infoRegularTask.GetLastStart());
                    if (i.GetRestartInterval() > delta) {
                        minDelta = Min<ui32>(minDelta, i.GetRestartInterval().Seconds() - delta.Seconds());
                    } else {
                        minDelta = 0;
                    }
                }
                CHECK_WITH_LOG(minDelta != Max<ui32>());
                INFO_LOG << "Sleep for " << Max<ui32>(1, minDelta) << " seconds..." << Endl;
                if (Active.WaitT(TDuration::Seconds(Max<ui32>(1, minDelta)))) {
                    break;
                }
            } catch (...) {
                ERROR_LOG << "While TExecutorRegular::Process: " << CurrentExceptionMessage() << Endl;
            }
        }
    }
}
