/*
 * Copyright (c) 2024-2026 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 "bgtask_config.h"
#include "data_storage_helper.h"
#include "res_sched_signature_validator.h"
#include "bgtaskmgr_log_wrapper.h"

#ifdef BGTASK_MGR_UNIT_TEST
#define WEAK_FUNC __attribute__((weak))
#else
#define WEAK_FUNC
#endif

namespace OHOS {
namespace BackgroundTaskMgr {
namespace {
const std::string SUSPEND_MANAGER_CONFIG_FILE = "etc/efficiency_manager/suspend_manager_config.json";
const std::string CONFIG_JSON_INDEX_TOP = "params";
const std::string CONFIG_JSON_INDEX_SUSPEND_SECOND = "param";
const std::string TRANSIENT_ERR_DELAYED_FROZEN_LIST = "transient_err_delayed_frozen_list";
const std::string CONTINUOUS_TASK_KEEPING_EXEMPTED_LIST = "continuous_task_keeping_exemption_list";
const std::string MALICIOUS_APP_BLOCKLIST = "malicious_app_blocklist";
const std::string TRANSIENT_EXEMPTED_QUOTA = "transient_exempted_quota";
const std::string TRANSIENT_ERR_DELAYED_FROZEN_TIME = "transient_err_delayed_frozen_time";
const std::string CONTINUOUS_SPECIAL_EXEMPTED_LIST = "special_exempted_list";
const std::string BUNDLE_SIGNATURE = "bundle_signature";

const std::string BACKGROUND_TASK_CONFIG_FILE = "etc/backgroundtask/config.json";
const std::string CPU_EFFICIENCY_RESOURCE_ALLOW_APPLY_BUNDLE_INFOS = "cpu_efficiency_resource_allow_apply_bundle_infos";
const std::string ALLOW_APPLY_BUNDLE_INFO_BUNDLE_NAME = "bundleName";
const std::string ALLOW_APPLY_BUNDLE_INFO_APP_SIGNATURES = "appSignatures";
const std::string ALLOW_APPLY_BUNDLE_INFO_CPU_LEVEL = "cpuLevel";
}

void BgtaskConfig::Init()
{
    if (isInit_) {
        BGTASK_LOGE("already init config!");
        return;
    }

    LoadConfigFile();
    LoadBgTaskConfigFile();
    isInit_ = true;
}

void BgtaskConfig::LoadConfigFile()
{
    nlohmann::json jsonObj;
    std::string absolutePath = DelayedSingleton<DataStorageHelper>::GetInstance()->
        GetConfigFileAbsolutePath(SUSPEND_MANAGER_CONFIG_FILE);
    if (DelayedSingleton<DataStorageHelper>::GetInstance()->ParseJsonValueFromFile(jsonObj, absolutePath) != 0) {
        BGTASK_LOGE("LoadConfigFile failed");
        return;
    }
    ParseTransientTaskExemptedQuatoList(jsonObj);
    ParseTransientTaskExemptedQuato(jsonObj);
}

void BgtaskConfig::ParseTransientTaskExemptedQuatoList(const nlohmann::json &jsonObj)
{
    nlohmann::json appArray;
    if (jsonObj.is_null() || jsonObj.empty()) {
        BGTASK_LOGE("jsonObj null");
        return;
    }
    if (!jsonObj.contains(TRANSIENT_ERR_DELAYED_FROZEN_LIST) ||
        !jsonObj[TRANSIENT_ERR_DELAYED_FROZEN_LIST].is_array()) {
        BGTASK_LOGE("no key %{public}s", TRANSIENT_ERR_DELAYED_FROZEN_LIST.c_str());
        return;
    }
    appArray = jsonObj[TRANSIENT_ERR_DELAYED_FROZEN_LIST];
    std::lock_guard<std::mutex> lock(configMutex_);
    for (const auto &app : appArray) {
        transientTaskExemptedQuatoList_.insert(app);
    }
    for (const auto &app : transientTaskExemptedQuatoList_) {
        BGTASK_LOGI("ParseTransientTaskExemptedQuatoList: %{public}s.", app.c_str());
    }
}

bool BgtaskConfig::AddExemptedQuatoData(const std::string &configData, int32_t sourceType)
{
    const nlohmann::json &jsonObj = nlohmann::json::parse(configData, nullptr, false);
    if (jsonObj.is_discarded()) {
        BGTASK_LOGE("jsonObj parse fail");
        return false;
    }
    if (jsonObj.is_null() || jsonObj.empty()) {
        BGTASK_LOGE("jsonObj null");
        return false;
    }
    if (sourceType == ConfigDataSourceType::CONFIG_CLOUD && !SetCloudConfigParam(jsonObj)) {
        return false;
    } else if (sourceType == ConfigDataSourceType::CONFIG_SUSPEND_MANAGER) {
        nlohmann::json appArray;
        if (!jsonObj.contains(TRANSIENT_ERR_DELAYED_FROZEN_LIST) ||
            !jsonObj[TRANSIENT_ERR_DELAYED_FROZEN_LIST].is_array()) {
            BGTASK_LOGE("no key %{public}s", TRANSIENT_ERR_DELAYED_FROZEN_LIST.c_str());
            return false;
        }
        appArray = jsonObj[TRANSIENT_ERR_DELAYED_FROZEN_LIST];
        std::lock_guard<std::mutex> lock(configMutex_);
        transientTaskExemptedQuatoList_.clear();
        for (const auto &app : appArray) {
            transientTaskExemptedQuatoList_.insert(app);
        }
        for (const auto &appName : transientTaskExemptedQuatoList_) {
            BGTASK_LOGI("transientTaskExemptedQuatoList appName: %{public}s", appName.c_str());
        }

        if (!jsonObj.contains(TRANSIENT_ERR_DELAYED_FROZEN_TIME) ||
            !jsonObj[TRANSIENT_ERR_DELAYED_FROZEN_TIME].is_number_integer()) {
            BGTASK_LOGE("no key %{public}s", TRANSIENT_ERR_DELAYED_FROZEN_TIME.c_str());
            return false;
        }
        transientTaskExemptedQuato_ = jsonObj[TRANSIENT_ERR_DELAYED_FROZEN_TIME].get<int>();
        BGTASK_LOGI("suspend config transientTaskExemptedQuato: %{public}d", transientTaskExemptedQuato_);
    }
    return true;
}

bool BgtaskConfig::SetCloudConfigParam(const nlohmann::json &jsonObj)
{
    if (!jsonObj.contains(CONFIG_JSON_INDEX_TOP) || !jsonObj[CONFIG_JSON_INDEX_TOP].is_object()) {
        BGTASK_LOGE("no key %{public}s", CONFIG_JSON_INDEX_TOP.c_str());
        return false;
    }
    nlohmann::json params = jsonObj[CONFIG_JSON_INDEX_TOP];
    {
        std::lock_guard<std::mutex> lock(configMutex_);
        SetTransientTaskParam(params);
        SetContinuousTaskParam(params);
    }
    ParseBundleSignature(params);
    return true;
}

void BgtaskConfig::SetTransientTaskParam(const nlohmann::json &jsonObj)
{
    if (jsonObj.contains(TRANSIENT_ERR_DELAYED_FROZEN_LIST) &&
        jsonObj[TRANSIENT_ERR_DELAYED_FROZEN_LIST].is_array()) {
        nlohmann::json appArray = jsonObj[TRANSIENT_ERR_DELAYED_FROZEN_LIST];
        transientTaskCloudExemptedQuatoList_.clear();
        for (const auto &app : appArray) {
            transientTaskCloudExemptedQuatoList_.insert(app);
        }
        for (const auto &appName : transientTaskCloudExemptedQuatoList_) {
            BGTASK_LOGI("transientTaskCloudExemptedQuatoList appName: %{public}s", appName.c_str());
        }
    } else {
        BGTASK_LOGW("no key %{public}s", TRANSIENT_ERR_DELAYED_FROZEN_LIST.c_str());
    }
    if (jsonObj.contains(CONFIG_JSON_INDEX_SUSPEND_SECOND) &&
        jsonObj[CONFIG_JSON_INDEX_SUSPEND_SECOND].is_object()) {
        nlohmann::json param = jsonObj[CONFIG_JSON_INDEX_SUSPEND_SECOND];
        if (param.contains(TRANSIENT_EXEMPTED_QUOTA) &&
            !param[TRANSIENT_EXEMPTED_QUOTA].is_number_integer()) {
            transientTaskExemptedQuato_ = param[TRANSIENT_EXEMPTED_QUOTA].get<int>();
            BGTASK_LOGI("cloud config transientTaskExemptedQuato: %{public}d", transientTaskExemptedQuato_);
        } else {
            BGTASK_LOGE("no key %{public}s", TRANSIENT_EXEMPTED_QUOTA.c_str());
        }
    } else {
        BGTASK_LOGW("no key %{public}s", CONFIG_JSON_INDEX_SUSPEND_SECOND.c_str());
    }
}

void BgtaskConfig::SetContinuousTaskParam(const nlohmann::json &jsonObj)
{
    if (jsonObj.contains(CONTINUOUS_TASK_KEEPING_EXEMPTED_LIST) &&
        jsonObj[CONTINUOUS_TASK_KEEPING_EXEMPTED_LIST].is_array()) {
        nlohmann::json appArrayTaskKeeping = jsonObj[CONTINUOUS_TASK_KEEPING_EXEMPTED_LIST];
        taskKeepingExemptedQuatoList_.clear();
        for (const auto &app : appArrayTaskKeeping) {
            taskKeepingExemptedQuatoList_.insert(app);
        }
        for (const auto &appName : taskKeepingExemptedQuatoList_) {
            BGTASK_LOGI("taskKeepingExemptedQuatoList_ appName: %{public}s", appName.c_str());
        }
    } else {
        BGTASK_LOGW("no key %{public}s", CONTINUOUS_TASK_KEEPING_EXEMPTED_LIST.c_str());
    }
    if (jsonObj.contains(MALICIOUS_APP_BLOCKLIST) &&
        jsonObj[MALICIOUS_APP_BLOCKLIST].is_array()) {
        nlohmann::json appArrayMalicious = jsonObj[MALICIOUS_APP_BLOCKLIST];
        maliciousAppBlocklist_.clear();
        for (const auto &app : appArrayMalicious) {
            maliciousAppBlocklist_.insert(app);
        }
        for (const auto &appName : maliciousAppBlocklist_) {
            BGTASK_LOGI("maliciousAppBlocklist_ appName: %{public}s", appName.c_str());
        }
    } else {
        BGTASK_LOGW("no key %{public}s", MALICIOUS_APP_BLOCKLIST.c_str());
    }
}

void BgtaskConfig::ParseTransientTaskExemptedQuato(const nlohmann::json &jsonObj)
{
    if (jsonObj.is_null() || jsonObj.empty()) {
        BGTASK_LOGE("jsonObj null");
        return;
    }
    if (!jsonObj.contains(TRANSIENT_EXEMPTED_QUOTA) || !jsonObj[TRANSIENT_EXEMPTED_QUOTA].is_number_integer()) {
        BGTASK_LOGE("no key %{public}s", TRANSIENT_EXEMPTED_QUOTA.c_str());
        return;
    }
    std::lock_guard<std::mutex> lock(configMutex_);
    transientTaskExemptedQuato_ = jsonObj[TRANSIENT_EXEMPTED_QUOTA].get<int32_t>();
    BGTASK_LOGI("transientTaskExemptedQuato_ %{public}d", transientTaskExemptedQuato_);
}

bool BgtaskConfig::IsTransientTaskExemptedQuatoApp(const std::string &bundleName)
{
    bool isExempt = false;
    {
        std::lock_guard<std::mutex> lock(configMutex_);
        if (transientTaskCloudExemptedQuatoList_.size() > 0) {
            isExempt = transientTaskCloudExemptedQuatoList_.count(bundleName) > 0;
        } else {
            isExempt = transientTaskExemptedQuatoList_.count(bundleName) > 0;
        }
    }
    return isExempt && CheckSignature(bundleName);
}

bool BgtaskConfig::IsTaskKeepingExemptedQuatoApp(const std::string &bundleName)
{
    {
        std::lock_guard<std::mutex> lock(configMutex_);
        if (!taskKeepingExemptedQuatoList_.count(bundleName)) {
            return false;
        }
    }
    return CheckSignature(bundleName);
}

int32_t BgtaskConfig::GetTransientTaskExemptedQuato()
{
    std::lock_guard<std::mutex> lock(configMutex_);
    return transientTaskExemptedQuato_;
}

void BgtaskConfig::SetSupportedTaskKeepingProcesses(const std::set<std::string> &processSet)
{
    std::lock_guard<std::mutex> lock(configMutex_);
    taskKeepingExemptedQuatoList_.insert(processSet.begin(), processSet.end());
    for (const auto &item : taskKeepingExemptedQuatoList_) {
        BGTASK_LOGI("taskKeeping Exemption proc: %{public}s", item.c_str());
    }
}

void BgtaskConfig::SetMaliciousAppConfig(const std::set<std::string> &maliciousAppSet)
{
    std::lock_guard<std::mutex> lock(configMutex_);
    maliciousAppBlocklist_.insert(maliciousAppSet.begin(), maliciousAppSet.end());
    for (const auto &item : maliciousAppBlocklist_) {
        BGTASK_LOGI("malicious app blocklist proc: %{public}s", item.c_str());
    }
}

bool BgtaskConfig::IsMaliciousAppConfig(const std::string &bundleName)
{
    {
        std::lock_guard<std::mutex> lock(configMutex_);
        if (!maliciousAppBlocklist_.count(bundleName)) {
            return false;
        }
    }
    return CheckSignature(bundleName);
}

void BgtaskConfig::LoadBgTaskConfigFile()
{
    nlohmann::json jsonObj;
    std::string absolutePath = DelayedSingleton<DataStorageHelper>::GetInstance()->
        GetConfigFileAbsolutePath(BACKGROUND_TASK_CONFIG_FILE);
    if (DelayedSingleton<DataStorageHelper>::GetInstance()->ParseJsonValueFromFile(jsonObj, absolutePath) != 0) {
        BGTASK_LOGE("LoadBgTaskConfigFile failed");
        return;
    }
    ParseCpuEfficiencyResourceApplyBundleInfos(jsonObj);
}

void BgtaskConfig::ParseBundleSignature(const nlohmann::json &jsonObj)
{
    if (!jsonObj.contains(BUNDLE_SIGNATURE) || !jsonObj[BUNDLE_SIGNATURE].is_object()) {
        BGTASK_LOGE("no contain bundle.");
        return;
    }
    nlohmann::json bundleList = jsonObj[BUNDLE_SIGNATURE];
    std::unordered_map<std::string, std::string> configs;
    for (const auto &info : bundleList.items()) {
        if (info.value().is_string()) {
            configs.emplace(info.key(), info.value().get<std::string>());
        }
    }
    BGTASK_LOGI("bundle size:%{public}u", (uint32_t)configs.size());
    ResourceSchedule::ResSchedSignatureValidator::GetInstance().AddSignatureConfig(configs);
}

bool BgtaskConfig::CheckSignature(const std::string &bundlename) const
{
    return ResourceSchedule::ResSchedSignatureValidator::GetInstance().CheckSignatureByBundleName(bundlename) ==
           ResourceSchedule::SignatureCheckResult::CHECK_OK;
}

void BgtaskConfig::ParseCpuEfficiencyResourceApplyBundleInfos(const nlohmann::json &jsonObj)
{
    if (jsonObj.is_null() || jsonObj.empty()) {
        BGTASK_LOGE("%{public}s jsonObj null", __func__);
        return;
    }
    if (!jsonObj.contains(CPU_EFFICIENCY_RESOURCE_ALLOW_APPLY_BUNDLE_INFOS) ||
        !jsonObj[CPU_EFFICIENCY_RESOURCE_ALLOW_APPLY_BUNDLE_INFOS].is_array()) {
        BGTASK_LOGE("no key %{public}s", CPU_EFFICIENCY_RESOURCE_ALLOW_APPLY_BUNDLE_INFOS.c_str());
        return;
    }

    nlohmann::json allowApplyCpuLevelBundleInfos = jsonObj[CPU_EFFICIENCY_RESOURCE_ALLOW_APPLY_BUNDLE_INFOS];
    std::lock_guard<std::mutex> lock(configMutex_);
    for (const auto &bundleInfoJsonObj : allowApplyCpuLevelBundleInfos) {
        if (!bundleInfoJsonObj.contains(ALLOW_APPLY_BUNDLE_INFO_BUNDLE_NAME) ||
            !bundleInfoJsonObj[ALLOW_APPLY_BUNDLE_INFO_BUNDLE_NAME].is_string()) {
            BGTASK_LOGE("prop %{public}s invalid", ALLOW_APPLY_BUNDLE_INFO_BUNDLE_NAME.c_str());
            continue;
        }
        if (!bundleInfoJsonObj.contains(ALLOW_APPLY_BUNDLE_INFO_CPU_LEVEL) ||
            !bundleInfoJsonObj[ALLOW_APPLY_BUNDLE_INFO_CPU_LEVEL].is_number_integer()) {
            BGTASK_LOGE("prop %{public}s invalid", ALLOW_APPLY_BUNDLE_INFO_CPU_LEVEL.c_str());
            continue;
        }
        if (!bundleInfoJsonObj.contains(ALLOW_APPLY_BUNDLE_INFO_APP_SIGNATURES) ||
            !bundleInfoJsonObj[ALLOW_APPLY_BUNDLE_INFO_APP_SIGNATURES].is_array()) {
            BGTASK_LOGE("prop %{public}s invalid", ALLOW_APPLY_BUNDLE_INFO_APP_SIGNATURES.c_str());
            continue;
        }

        std::vector<std::string> appSignatures;
        nlohmann::json signArrayJsonObj = bundleInfoJsonObj[ALLOW_APPLY_BUNDLE_INFO_APP_SIGNATURES];
        for (const auto &appSign : signArrayJsonObj) {
            if (!appSign.is_string()) {
                BGTASK_LOGE("prop %{public}s value invalid", ALLOW_APPLY_BUNDLE_INFO_APP_SIGNATURES.c_str());
                continue;
            }
            appSignatures.push_back(appSign.get<std::string>());
        }
        std::string bundleName = bundleInfoJsonObj[ALLOW_APPLY_BUNDLE_INFO_BUNDLE_NAME].get<std::string>();
        int32_t cpuLevel = bundleInfoJsonObj[ALLOW_APPLY_BUNDLE_INFO_CPU_LEVEL].get<int32_t>();
        bgTaskConfigFileInfo_.AddCpuLevelConfigInfo({bundleName, appSignatures, cpuLevel});
    }
    for (const auto &[bundleName, info] : bgTaskConfigFileInfo_.GetAllowApplyCpuBundleInfoMap()) {
        BGTASK_LOGI("%{public}s: bundleName %{public}s, cpuLevel %{public}d", __func__, bundleName.c_str(),
            info.cpuLevel);
    }
}

bool WEAK_FUNC BgtaskConfig::CheckRequestCpuLevelBundleNameConfigured(const std::string &bundleName)
{
    std::lock_guard<std::mutex> lock(configMutex_);
    return bgTaskConfigFileInfo_.CheckBundleName(bundleName);
}

bool WEAK_FUNC BgtaskConfig::CheckRequestCpuLevelAppSignatures(const std::string &bundleName, const std::string &appId,
    const std::string &appIdentifier)
{
    std::lock_guard<std::mutex> lock(configMutex_);
    return bgTaskConfigFileInfo_.CheckAppSignatures(bundleName, appId, appIdentifier);
}

bool WEAK_FUNC BgtaskConfig::CheckRequestCpuLevel(const std::string &bundleName, int32_t cpuLevel)
{
    std::lock_guard<std::mutex> lock(configMutex_);
    return bgTaskConfigFileInfo_.CheckCpuLevel(bundleName, cpuLevel);
}

void BgtaskConfig::SetSpecialExemptedProcess(const std::set<std::string> &bundleNameSet)
{
    std::lock_guard<std::mutex> lock(configMutex_);
    specialExemptedQuatoList_.insert(bundleNameSet.begin(), bundleNameSet.end());
    for (const auto &item : specialExemptedQuatoList_) {
        BGTASK_LOGI("special Exemption proc: %{public}s", item.c_str());
    }
}

bool BgtaskConfig::IsSpecialExemptedQuatoApp(const std::string &bundleName)
{
    {
        std::lock_guard<std::mutex> lock(configMutex_);
        if (!specialExemptedQuatoList_.count(bundleName)) {
            return false;
        }
    }
    return CheckSignature(bundleName);
}
}
}