/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "updater/hardware_fault_retry.h"
#include <unistd.h>
#include "init_reboot.h"
#include "log/log.h"
#include "misc_info/misc_info.h"
#include "updater/updater.h"
#include "updater/updater_const.h"
#include "updater_init.h"
#include "utils.h"
#include "securec.h"

namespace Updater {
HardwareFaultRetry &HardwareFaultRetry::GetInstance()
{
    static HardwareFaultRetry instance;
    return instance;
}

HardwareFaultRetry::HardwareFaultRetry()
{
    RetryFunc rebootFunc = [this]() {
                                return this->RebootRetry();
                            };
    RegisterFunc(VERIFY_FAILED_REBOOT, rebootFunc);
    RegisterFunc(IO_FAILED_REBOOT, rebootFunc);
    RegisterFunc(BLOCK_UPDATE_FAILED_REBOOT, rebootFunc);
}

void HardwareFaultRetry::RegisterFunc(const std::string &faultInfo, RetryFunc func)
{
    if (!retryMap_.emplace(faultInfo, func).second) {
        LOG(ERROR) << "emplace: " << faultInfo.c_str() << " fail";
    }
}

void HardwareFaultRetry::RegisterDefaultFunc(const std::string &faultInfo)
{
    if (!retryMap_.emplace(faultInfo, [this]() {
                                return this->RebootRetry();
                            }).second) {
        LOG(ERROR) << "emplace: " << faultInfo.c_str() << " fail";
    }
}

void HardwareFaultRetry::RemoveFunc(const std::string &faultInfo)
{
    if (retryMap_.erase(faultInfo) == 0) {
        LOG(ERROR) << "erase " << faultInfo.c_str() << " fail";
    }
}

void HardwareFaultRetry::DoRetryAction()
{
    auto it = retryMap_.find(faultInfo_);
    if (it == retryMap_.end() || it->second == nullptr) {
        LOG(ERROR) << "GetRepair func for: " << faultInfo_.c_str() << " fail";
        return;
    }
    return (it->second)();
}

void HardwareFaultRetry::SetFaultInfo(const std::string &faultInfo)
{
    faultInfo_ = faultInfo;
}

void HardwareFaultRetry::SetRetryCount(const uint32_t count)
{
    retryCount_ = count;
}

void HardwareFaultRetry::SetEffectiveValue(bool value)
{
    effective_ = value;
}

void HardwareFaultRetry::SetRebootCmd(const std::string &rebootCmd)
{
    rebootCmd_ = rebootCmd;
}

bool HardwareFaultRetry::IsRetry(void)
{
    return isRetry_;
}

void HardwareFaultRetry::RebootRetry()
{
    if (!effective_) {
        LOG(WARNING) << "Special scenarios do not take effect, not need retry.";
        return;
    }
    if (faultInfo_ == VERIFY_FAILED_REBOOT) {
        UpdaterInit::GetInstance().InvokeEvent(UPDATER_PACKAGES_VERIFY_FAILED_EVENT);
    }
    if (retryCount_ >= MAX_RETRY_COUNT) {
        LOG(INFO) << "retry more than 3 times, no need retry";
        return;
    }
    LOG(INFO) << "enter into reboot retry";
    Utils::AddUpdateInfoToMisc("retry_count", retryCount_ + 1);
    Utils::SetFaultInfoToMisc(faultInfo_);
    isRetry_ = true;

    PostUpdater(false);
    sync();
#ifndef UPDATER_UT
    if (rebootCmd_.empty()) {
        DoReboot("updater:Updater fault retry");
    } else {
        DoReboot(rebootCmd_.c_str());
    }
    while (true) {
        pause();
    }
#endif
}
} // Updater