/**
 * 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 "dump.h"

#include <mutex>
#include <sstream>
#include <set>

#include "mmpa/mmpa_api.h"
#include "adx_datadump_server.h"
#include "adump_pub.h"

#include "common/json_parser.h"
#include "common/error_codes_inner.h"

#include "common/log_inner.h"
#include "utils/string_utils.h"
#include "aclrt_impl/acl_rt_impl_base.h"
#include "acl_rt_impl.h"

namespace {
    bool aclmdlInitDumpFlag = false;
    std::mutex aclDumpMutex;
    constexpr int32_t ADX_ERROR_NONE = 0;
}

namespace acl {
    AclDump &AclDump::GetInstance()
    {
        static AclDump aclDumpProc;
        return aclDumpProc;
    }

    aclError AclDump::HandleDumpCommand(const char *configStr, size_t size, const char *configPath)
    {
        ACL_LOG_INFO("start to execute HandleDumpCommand.");

        int32_t adxRet = AdxDataDumpServerInit();
        if (adxRet != ADX_ERROR_NONE) {
            ACL_LOG_INNER_ERROR("[AdxDataDumpServer][Init]dump server run failed, adx errorCode = %d", adxRet);
            return ACL_ERROR_INTERNAL_ERROR;
        }
        acl::AclDump::GetInstance().SetAdxInitFromAclInitFlag(true);

        // base dump
        Adx::DumpConfigInfo configInfo;
        configInfo.dumpConfigPath = configPath;
        configInfo.dumpConfigData = configStr;
        configInfo.dumpConfigSize = size;
        adxRet = Adx::AdumpSetDumpConfig(configInfo);
        if (adxRet != ADX_ERROR_NONE) {
            auto ret =
                (adxRet == Adx::ADUMP_INPUT_FAILED) ? ACL_ERROR_INVALID_DUMP_CONFIG : ACL_ERROR_INTERNAL_ERROR;
            ACL_LOG_INNER_ERROR("[Set][Dump]set dump config failed, adx errorCode = %d", adxRet);
            return ret;
        }

        return ACL_SUCCESS;
    }

    aclError AclDump::HandleDumpConfig(const char_t *const configPath)
    {
        ACL_LOG_INFO("start to execute HandleDumpConfig.");
        std::string configStr;
        aclError ret = acl::JsonParser::GetConfigStrFromFile(configPath, configStr);
        if (ret != ACL_SUCCESS) {
            ACL_LOG_INNER_ERROR("Get config string from file[%s] failed, errorCode = %d",
                configPath, ret);
            return ret;
        }
        try {
            if (!configStr.empty()) {
                return HandleDumpCommand(configStr.c_str(), configStr.size(), configPath);
            }
        } catch (const nlohmann::json::exception &e) {
            ACL_LOG_INNER_ERROR("[Convert][DumpConfig]parse json for config failed, exception:%s.",
                e.what());
            return ACL_ERROR_INVALID_DUMP_CONFIG;
        }
        ACL_LOG_INFO("HandleDumpConfig end in HandleDumpConfig.");
        return ACL_SUCCESS;
    }
} // namespace acl

#ifdef __cplusplus
extern "C" {
#endif

aclError aclmdlInitDumpImpl()
{
    ACL_LOG_INFO("start to execute aclmdlInitDump.");
    if (!acl::GetAclInitFlag()) {
        acl::AclErrorLogManager::ReportInputError(
            "EP0008", std::vector<const char*>({"func", "reason"}),
            std::vector<const char*>(
                {"aclmdlInitDump", "aclInit must be executed before aclmdlInitDump is called"}));
        ACL_LOG_ERROR("[Check][AclInitFlag]aclInit must be executed before aclmdlInitDump is called");
        return ACL_ERROR_UNINITIALIZE;
    }

    const std::unique_lock<std::mutex> lk(aclDumpMutex);
    if (aclmdlInitDumpFlag) {
        acl::AclErrorLogManager::ReportInputError(
            "EP0008", std::vector<const char*>({"func", "reason"}),
            std::vector<const char*>({"aclmdlInitDump", "This API cannot be called repeatedly"}));
        ACL_LOG_ERROR("[Check][InitDumpFlag]This API cannot be called repeatedly");
        return ACL_ERROR_REPEAT_INITIALIZE;
    }

    const int32_t adxRet = AdxDataDumpServerInit();
    if (adxRet != ADX_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("[AdxDataDumpServer][Init]dump server run failed, adx errorCode = %d", adxRet);
        return ACL_ERROR_INTERNAL_ERROR;
    }

    aclmdlInitDumpFlag = true;
    ACL_LOG_INFO("successfully initialized dump in aclmdlInitDump.");
    return ACL_SUCCESS;
}

aclError aclmdlSetDumpImpl(const char *dumpCfgPath)
{
    ACL_LOG_INFO("start to execute aclmdlSetDump.");
    if (!acl::GetAclInitFlag()) {
        acl::AclErrorLogManager::ReportInputError(
            "EP0008", std::vector<const char*>({"func", "reason"}),
            std::vector<const char*>(
                {"aclmdlSetDump", "aclInit must be executed before aclmdlSetDumpImpl is called"}));
        ACL_LOG_ERROR("[Check][AclInitFlag]aclInit must be executed before aclmdlSetDumpImpl is called");
        return ACL_ERROR_UNINITIALIZE;
    }

    const std::unique_lock<std::mutex> lk(aclDumpMutex);
    if (!aclmdlInitDumpFlag) {
        acl::AclErrorLogManager::ReportInputError(
            "EP0008", std::vector<const char*>({"func", "reason"}),
            std::vector<const char*>(
                {"aclmdlSetDump", "aclmdlInitDump must be executed before aclmdlSetDumpImpl is called"}));
        ACL_LOG_ERROR("[Check][aclmdlInitDumpFlag]aclmdlInitDump must be executed before aclmdlSetDumpImpl is called");
        return ACL_ERROR_DUMP_NOT_RUN;
    }

    ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(dumpCfgPath);
    std::string configStr;
    aclError ret = acl::JsonParser::GetConfigStrFromFile(dumpCfgPath, configStr);
    if (ret != ACL_SUCCESS) {
        ACL_LOG_INNER_ERROR("Get config string from file[%s] failed, errorCode = %d",
            dumpCfgPath, ret);
        return ret;
    }

    // base dump
    if (!configStr.empty()) {
        ACL_LOG_INFO("Start to set dump.");
        Adx::DumpConfigInfo configInfo;
        configInfo.dumpConfigPath = dumpCfgPath;
        configInfo.dumpConfigData = configStr.c_str();
        configInfo.dumpConfigSize = configStr.size();
        const auto adxRet = Adx::AdumpSetDumpConfig(configInfo);
        if (adxRet != ADX_ERROR_NONE) {
            ret =
                (adxRet == Adx::ADUMP_INPUT_FAILED) ? ACL_ERROR_INVALID_DUMP_CONFIG : ACL_ERROR_INTERNAL_ERROR;
            ACL_LOG_INNER_ERROR("[Set][Dump]set dump config failed, adx errorCode = %d", adxRet);
            return ret;
        }
    }
    ACL_LOG_INFO("set dump config successfully.");
    return ACL_SUCCESS;
}

aclError aclmdlFinalizeDumpImpl()
{
    ACL_LOG_INFO("start to execute aclmdlFinalizeDump.");
    if (!acl::GetAclInitFlag()) {
        acl::AclErrorLogManager::ReportInputError(
            "EP0008", std::vector<const char*>({"func", "reason"}),
            std::vector<const char*>(
                {"aclmdlFinalizeDump", "aclInit must be executed before aclmdlFinalizeDump is called"}));
        ACL_LOG_ERROR("[Check][AclInitFlag]aclInit must be executed before aclmdlFinalizeDump is called");
        return ACL_ERROR_UNINITIALIZE;
    }

    const std::unique_lock<std::mutex> lk(aclDumpMutex);
    if (!aclmdlInitDumpFlag) {
        acl::AclErrorLogManager::ReportInputError(
            "EP0008", std::vector<const char*>({"func", "reason"}),
            std::vector<const char*>(
                {"aclmdlFinalizeDump", "aclmdlInitDump must be executed before aclmdlFinalizeDump is called"}));
        ACL_LOG_ERROR("[Check][aclmdlInitDumpFlag]aclmdlInitDump must be executed before aclmdlFinalizeDump is called");
        return ACL_ERROR_DUMP_NOT_RUN;
    }

    // close adump opened
    int32_t adxRet = Adx::AdumpUnSetDump();
    if (adxRet != ADX_ERROR_NONE) {
        ACL_LOG_INNER_ERROR("[Set][Dump]set dump off failed, adx errorCode = %d", adxRet);
        return ACL_ERROR_INTERNAL_ERROR;
    }

    // close dump server
    adxRet = AdxDataDumpServerUnInit();
    if (adxRet != ADX_ERROR_NONE) {
        ACL_LOG_CALL_ERROR("[AdxDataDumpServer][UnInit]generate dump file failed in disk, adx errorCode = %d", adxRet);
        return ACL_ERROR_INTERNAL_ERROR;
    }

    aclmdlInitDumpFlag = false;
    ACL_LOG_INFO("successfully execute aclmdlFinalizeDump, the dump task completed!");
    return ACL_SUCCESS;
}
#ifdef __cplusplus
}
#endif