/**
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
 * CANN Open Software License Agreement Version 2.0 (the "License").
 * Please refer to the License for details. You may not use this file except in compliance with the License.
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
 * See LICENSE in the root of the software repository for the full text of the License.
 */

#include <sstream>
#include "qs_args_parser.h"
#include "bqs_util.h"
#include "bqs_log.h"
#include "bqs_feature_ctrl.h"

namespace bqs {
bool ArgsParser::ParseArgs(const int32_t argc, const char_t* const argv[])
{
    if ((argv == nullptr) || (argc <= 1)) {
        return false;
    }

    for (uint32_t i = 0; i < static_cast<uint32_t>(argc); ++i) {
        if (!ParseSinglePara(std::string(argv[i]))) {
            return false;
        }
    }

    return CheckRequiredParas();
}

bool ArgsParser::ParseArgs(const std::vector<std::string>& args)
{
    for (const auto& iter : args) {
        if (!ParseSinglePara(iter)) {
            return false;
        }
    }

    return CheckRequiredParas();
}

bool ArgsParser::CheckRequiredParas() const
{
    std::unordered_map<std::string, bool> requiredParamsMap;
    requiredParamsMap.insert(std::make_pair(PARAM_DEVICEID, withDeviceId_));
    requiredParamsMap.insert(std::make_pair(PARAM_HOST_PID, withHostPid_));
    if (bqs::GetRunContext() == bqs::RunContext::HOST) {
        requiredParamsMap.insert(std::make_pair(PARAM_GRP_NAME, withGroupName_));
    }
    for (auto requiredParams : requiredParamsMap) {
        if (!requiredParams.second) {
            BQS_LOG_ERROR("Param error. Must specify param with [%s].", requiredParams.first.c_str());
            return false;
        }
    }

    std::unordered_map<std::string, bool> requiredParamsWarnMap;
    requiredParamsWarnMap.insert(std::make_pair(PARAM_VFID, withVfId_));
    requiredParamsWarnMap.insert(std::make_pair(PARAM_PIDSIGN, withPidSign_));
    for (auto requiredParamsWarn : requiredParamsWarnMap) {
        if (!requiredParamsWarn.second) {
            BQS_LOG_WARN("param error. Param %s does not exist.", requiredParamsWarn.first.c_str());
        }
    }

    return true;
}

bool ArgsParser::ParseSinglePara(const std::string& singlePara)
{
    const std::size_t offset = singlePara.find("=");
    if (offset == std::string::npos) {
        return true;
    }

    const std::string key = singlePara.substr(0U, offset + 1UL);
    const std::string val = singlePara.substr(offset + 1UL);
    const auto iter = argsParseFuncMap_.find(key);
    if (iter == argsParseFuncMap_.end()) {
        // Ignore unknown parameter
        BQS_LOG_WARN("unknown input parameter [%s] of function ParseArgs, ignore", key.c_str());
        return true;
    }

    const bool ret = (this->*(iter->second))(val);
    if (!ret) {
        return false;
    }

    return true;
}

bool ArgsParser::ParseDeviceId(const std::string& para)
{
    int32_t val = 0;
    if (!TransStrToInt(para, val)) {
        BQS_LOG_ERROR("Parse deviceId failed, param is %s", para.c_str());
        return false;
    }

    if ((val < 0) || (val >= QS_FW_CHIP_NUM_MAX)) {
        BQS_LOG_ERROR("DeviceId param[%s] invalid, value is not in [%d, %d)", para.c_str(), 0, QS_FW_CHIP_NUM_MAX);
        return false;
    }

    deviceId_ = static_cast<uint32_t>(val);
    withDeviceId_ = true;
    return true;
}

bool ArgsParser::ParseDeviceId(const std::string& para, int32_t& deviceId) const
{
    int32_t val = 0;
    if (!TransStrToInt(para, val)) {
        BQS_LOG_WARN("Parse deviceId failed, param is %s", para.c_str());
        return false;
    }

    if ((val < 0) || (val >= QS_FW_CHIP_NUM_MAX)) {
        BQS_LOG_WARN("DeviceId param[%s] invalid, value is not in [%d, %d)", para.c_str(), 0, QS_FW_CHIP_NUM_MAX);
        return false;
    }

    deviceId = static_cast<uint32_t>(val);
    return true;
}

bool ArgsParser::ParseHostPid(const std::string& para)
{
    int32_t val = 0;
    if (!TransStrToInt(para, val)) {
        BQS_LOG_ERROR("Parse pid failed, param is %s", para.c_str());
        return false;
    }

    if (val <= 0) {
        BQS_LOG_ERROR("Pid param[%s] value invalid", para.c_str());
        return false;
    }

    hostPid_ = static_cast<uint32_t>(val);
    withHostPid_ = true;
    return true;
}

bool ArgsParser::ParsePidSign(const std::string& para)
{
    pidSign_ = para;
    withPidSign_ = true;
    return true;
}

bool ArgsParser::ParseVfId(const std::string& para)
{
    int32_t val = 0;
    if (!TransStrToInt(para, val)) {
        BQS_LOG_ERROR("Parse vfId failed, param is %s", para.c_str());
        return false;
    }

    if ((val < 0) || (val > VF_ID_MAX)) {
        BQS_LOG_ERROR("VfId param[%s] invalid, value is not in [%u, %u]", para.c_str(), 0, VF_ID_MAX);
        return false;
    }

    vfId_ = static_cast<uint32_t>(val);
    withVfId_ = true;
    return true;
}

void ArgsParser::SetLogLevel(const int32_t logLevel, const int32_t eventLevel) const
{
    if (bqs::FeatureCtrl::IsHostQs()) {
        bqs::HostQsLog::GetInstance().DlogSetLevel(logLevel, eventLevel);
        LogAttr dlogAttrInfo = {};
        dlogAttrInfo.type = APPLICATION;
        dlogAttrInfo.pid = hostPid_;
        dlogAttrInfo.deviceId = deviceId_;
        dlogAttrInfo.mode = 0;
        bqs::HostQsLog::GetInstance().DlogSetAttr(dlogAttrInfo);
    } else {
        if (&dlog_setlevel != nullptr) {
            if (dlog_setlevel(-1, logLevel, eventLevel) != SUCCESS_VALUE) {
                BQS_LOG_WARN("Set log level failed");
            }
        }
        if (&DlogSetAttr != nullptr) {
            LogAttr dlogAttrInfo = {};
            dlogAttrInfo.type = APPLICATION;
            dlogAttrInfo.pid = hostPid_;
            dlogAttrInfo.deviceId = deviceId_;
            dlogAttrInfo.mode = 0;
            if (DlogSetAttr(dlogAttrInfo) != SUCCESS_VALUE) {
                BQS_LOG_WARN("Set log attr failed");
            }
        }
    }
}

void ArgsParser::SetAicpuLogLevel() const
{
    if ((aicpulogLevel_ >= DEBUG_LOG) && (aicpulogLevel_ <= ERROR_LOG)) {
        if (bqs::FeatureCtrl::IsHostQs()) {
            bqs::HostQsLog::GetInstance().DlogSetLevel(aicpulogLevel_, eventLevel_);
        } else {
            if (&dlog_setlevel != nullptr) {
                if (dlog_setlevel(AICPU, aicpulogLevel_, eventLevel_) != SUCCESS_VALUE) {
                    BQS_LOG_WARN("Set aicpu log level failed");
                }
            }
        }
    }
}

bool ArgsParser::ParseLogAndEventLevel(const std::string& para)
{
    int32_t val = 0;
    if (!TransStrToInt(para, val)) {
        return false;
    }

    if (val < 0) {
        return false;
    }

    logLevel_ = val % VALUE_FOR_CALCULATE_LOG_LEVEL;
    eventLevel_ = val / VALUE_FOR_CALCULATE_LOG_LEVEL;
    withLogLevel_ = true;
    SetLogLevel(logLevel_, eventLevel_);
    return true;
}

bool ArgsParser::ParseAicpuLogLevel(const std::string& para)
{
    int32_t val = 0;
    if (!TransStrToInt(para, val)) {
        return true;
    }

    aicpulogLevel_ = val;
    withLogLevel_ = true;
    SetAicpuLogLevel();
    return true;
}

bool ArgsParser::ParseDeployMode(const std::string& para)
{
    int32_t val = 0;
    if (!TransStrToInt(para, val)) {
        BQS_LOG_ERROR("Parse deploy mode failed, param is %s", para.c_str());
        return false;
    }

    if ((val < static_cast<int32_t>(bqs::QueueSchedulerRunMode::SINGLE_PROCESS)) ||
        (val > static_cast<int32_t>(bqs::QueueSchedulerRunMode::MULTI_THREAD))) {
        BQS_LOG_ERROR(
            "deploy mode param[%s] invalid, value is not in [%u, %u]", para.c_str(),
            static_cast<uint32_t>(QueueSchedulerRunMode::SINGLE_PROCESS),
            static_cast<uint32_t>(QueueSchedulerRunMode::MULTI_THREAD));
        return false;
    }

    deployMode_ = static_cast<QueueSchedulerRunMode>(val);
    return true;
}

// ignore error
bool ArgsParser::ParseReschedInterval(const std::string& para)
{
    int32_t val = 0;
    if (!TransStrToInt(para, val)) {
        BQS_LOG_WARN("Parse resched interval not success, using default value %d ms", reschedInterval_);
        return true;
    }

    if ((val < RESCHED_INTERVAL_CLOSE) || (val > RESCHED_INTERVAL_MAX)) {
        BQS_LOG_WARN(
            "resched interval param[%s] invalid, value is not in [%d, %d], using default value[%d] ms", para.c_str(),
            RESCHED_INTERVAL_CLOSE, RESCHED_INTERVAL_MAX, reschedInterval_);
        return true;
    }

    reschedInterval_ = val;
    return true;
}

bool ArgsParser::ParseGroupName(const std::string& para)
{
    groupName_ = para;
    withGroupName_ = true;
    return true;
}

bool ArgsParser::ParseSchedPolicy(const std::string& para)
{
    uint64_t val = 0;
    if (!TransStrToull(para, val)) {
        BQS_LOG_ERROR("Parse SchedPolicy failed, param is %s", para.c_str());
        return false;
    }

    BQS_LOG_INFO("SchedPolicy is [0x%lx].", val);
    schedPolicy_ = val;
    return true;
}

// ignore error
bool ArgsParser::ParseStarter(const std::string& para)
{
    int32_t val = 0;
    if (!TransStrToInt(para, val)) {
        BQS_LOG_WARN("Parse starter failed, param is %s", para.c_str());
        return true;
    }

    starter_ = static_cast<bqs::QsStartType>(val);
    withStarter_ = true;
    return true;
}

bool ArgsParser::ParseProfCfgData(const std::string& para)
{
    profCfgData_ = para;
    return true;
}

// ignore error
bool ArgsParser::ParseProfFlag(const std::string& para)
{
    int32_t val = 0;
    if (!TransStrToInt(para, val)) {
        BQS_LOG_WARN("Parse prof flag failed, param is %s", para.c_str());
        return true;
    }

    profFlag_ = (val != 0);
    return true;
}

// ignore error
bool ArgsParser::ParseAbnormalInterval(const std::string& para)
{
    int32_t val = 0;
    if (!TransStrToInt(para, val)) {
        BQS_LOG_INFO("Parse abnormal interval not success, using default value %d ms", abnormalInterval_);
        return true;
    }

    if ((val <= ABNORMAL_INTERVAL_MIN) || (val > ABNORMAL_INTERVAL_MAX)) {
        BQS_LOG_WARN(
            "abnormal interval param[%s] invalid, value is not in [%d, %d], using default value[%d]", para.c_str(),
            ABNORMAL_INTERVAL_MIN, ABNORMAL_INTERVAL_MAX, abnormalInterval_);
        return true;
    }

    abnormalInterval_ = val;
    return true;
}

// ignore error
bool ArgsParser::ParseResourceList(const std::string& para)
{
    size_t pos = para.find(",");
    if (pos == std::string::npos) {
        return true;
    }
    std::string deviceStr0 = para.substr(0, pos);
    std::string deviceStr1 = para.substr(pos + 1, para.length());
    int32_t curId = 0;
    if (!ParseDeviceId(deviceStr0, curId)) {
        return true;
    }
    resVec_.push_back(curId);

    if (!ParseDeviceId(deviceStr1, curId)) {
        return true;
    }
    resVec_.push_back(curId);
    return true;
}

// ignore error
bool ArgsParser::ParseDeviceIdsList(const std::string& para)
{
    std::istringstream issStr(para);
    std::string idStr;
    int32_t curId = 0;
    while (std::getline(issStr, idStr, ',')) {
        if (!ParseDeviceId(idStr, curId)) {
            return true;
        }
        devIdVec_.push_back(curId);
    }
    return true;
}

std::string ArgsParser::GetParaParsedStr()
{
    std::ostringstream oss;
    oss << "deviceId=" << deviceId_ << ", hostPid=" << hostPid_ << ", pidSign=" << pidSign_ << ", vfId=" << vfId_
        << ", logLevel=" << logLevel_ << ", eventLevel_=" << eventLevel_ << ", aicpulogLevel=" << aicpulogLevel_
        << ", deployMode_=" << static_cast<uint32_t>(deployMode_) << ", reschedInterval_=" << reschedInterval_
        << ", groupName_=" << groupName_ << ", schedPolicy_=" << schedPolicy_
        << ", starter_=" << static_cast<uint32_t>(starter_) << ", profCfgData_=" << profCfgData_
        << ", profFlag_=" << profFlag_ << ", abnormalInterval_=" << abnormalInterval_
        << ", withDeviceId_=" << withDeviceId_ << ", withHostPid_=" << withHostPid_ << ", withPidSign_=" << withPidSign_
        << ", withVfId_=" << withVfId_ << ", withLogLevel_=" << withLogLevel_ << ", withGroupName_=" << withGroupName_
        << ", withStarter_=" << withStarter_;

    return oss.str();
}
} // namespace bqs