/*
 * -------------------------------------------------------------------------
 * This file is part of the MindStudio project.
 * Copyright (c) 2025 Huawei Technologies Co.,Ltd.
 *
 * MindStudio is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *          http://license.coscl.org.cn/MulanPSL2
 *
 * 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 FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * -------------------------------------------------------------------------
 */

#include <utility>
#include <map>
#include <string>
#include <cstdlib>
#include <mutex>
#include <iostream>
#include <fstream>
#include <unordered_map>

#include "utils/FileUtils.h"
#include "ErrorInfosManager.h"

namespace MindStudioDebugger {

static std::mutex g_errInfoMtx;
DebuggerErrLevel ErrorInfosManager::topLevel = DebuggerErrLevel::LEVEL_NONE;
DebuggerErrLevel ErrorInfosManager::threshold = DebuggerErrLevel::LEVEL_INFO;

static std::map<DebuggerErrLevel, const char*> ErrorLevelString = {
    {DebuggerErrLevel::LEVEL_CRITICAL, "CRITICAL"},
    {DebuggerErrLevel::LEVEL_ERROR, "ERROR"},
    {DebuggerErrLevel::LEVEL_WARNING, "WARNING"},
    {DebuggerErrLevel::LEVEL_INFO, "INFO"},
    {DebuggerErrLevel::LEVEL_DEBUG, "DEBUG"},
    {DebuggerErrLevel::LEVEL_NONE, "NONE"},
};

static std::map<DebuggerErrno, const char*> ErrnoString = {
    {DebuggerErrno::OK, "OK"},
    {DebuggerErrno::ERROR, "ERROR"},

    {DebuggerErrno::ERROR_FILE_NOT_EXISTS, "FILE_NOT_EXISTS"},
    {DebuggerErrno::ERROR_FILE_ALREADY_EXISTS, "FILE_ALREADY_EXISTS"},
    {DebuggerErrno::ERROR_FAILED_TO_OPEN_FILE, "FAILED_TO_OPEN_FILE"},
    {DebuggerErrno::ERROR_FAILED_TO_WRITE_FILE, "FAILED_TO_WRITE_FILE"},
    {DebuggerErrno::ERROR_DIR_NOT_EXISTS, "DIR_NOT_EXISTS"},
    {DebuggerErrno::ERROR_PERMISSION_DENINED, "PERMISSION_DENINED"},
    {DebuggerErrno::ERROR_NOT_ALLOW_SOFTLINK, "NOT_ALLOW_SOFTLINK"},
    {DebuggerErrno::ERROR_ILLEGAL_FILE_TYPE, "ILLEGAL_FILE_TYPE"},
    {DebuggerErrno::ERROR_PATH_TOO_LOOG, "PATH_TOO_LOOG"},
    {DebuggerErrno::ERROR_PATH_TOO_DEEP, "PATH_TOO_DEEP"},
    {DebuggerErrno::ERROR_PATH_CONTAINS_INVALID_CHAR, "PATH_CONTAINS_INVALID_CHAR"},
    {DebuggerErrno::ERROR_FILE_TOO_LARGE, "FILE_TOO_LARGE"},
    {DebuggerErrno::ERROR_UNKNOWN_FILE_SUFFIX, "UNKNOWN_FILE_SUFFIX"},
    {DebuggerErrno::ERROR_CANNOT_PARSE_PATH, "CANNOT_PARSE_PATH"},

    {DebuggerErrno::ERROR_INVALID_OPERATION, "INVALID_OPERATION"},
    {DebuggerErrno::ERROR_INVALID_FORMAT, "INVALID_FORMAT"},
    {DebuggerErrno::ERROR_INVALID_VALUE, "INVALID_VALUE"},
    {DebuggerErrno::ERROR_UNKNOWN_FIELD, "UNKNOWN_FIELD"},
    {DebuggerErrno::ERROR_UNKNOWN_VALUE, "UNKNOWN_VALUE"},
    {DebuggerErrno::ERROR_UNKNOWN_TRANS, "UNKNOWN_TRANS"},
    {DebuggerErrno::ERROR_FIELD_NOT_EXISTS, "FIELD_NOT_EXISTS"},
    {DebuggerErrno::ERROR_VALUE_OVERFLOW, "VALUE_OVERFLOW"},

    {DebuggerErrno::ERROR_NO_MEMORY, "NO_MEMORY"},
    {DebuggerErrno::ERROR_BUFFER_OVERFLOW, "BUFFER_OVERFLOW"},
    {DebuggerErrno::ERROR_SYSCALL_FAILED, "SYSCALL_FAILED"},
    {DebuggerErrno::ERROR_OPERATION_FAILED, "OPERATION_FAILED"},

    {DebuggerErrno::ERROR_DEPENDENCY_NOT_FIND, "DEPENDENCY_NOT_FIND"},
    {DebuggerErrno::ERROR_EXTERNAL_API_ERROR, "EXTERNAL_API_ERROR"},
};


const std::unordered_map<std::string, std::string>& GetInvalidChar(void)
{
    static const std::unordered_map<std::string, std::string> INVALID_CHAR = {
        {"\n", "\\n"}, {"\f", "\\f"}, {"\r", "\\r"}, {"\b", "\\b"},
        {"\t", "\\t"}, {"\v", "\\v"}, {"\u007F", "\\u007F"}
    };
    return INVALID_CHAR;
}

// convert unsafe string to safe string
std::string ToSafeString(const std::string &str)
{
    std::string safeStr(str);
    const std::unordered_map<std::string, std::string> invalidChar = GetInvalidChar();
    size_t i = 0;
    while (i < safeStr.length()) {
        std::string chr(1, safeStr[i]);
        if (invalidChar.find(chr) != invalidChar.end()) {
            const std::string &validStr = invalidChar.at(chr);
            safeStr.replace(i, 1, validStr);
            i += validStr.length();
            continue;
        }
        i++;
    }
    return safeStr;
}


void ErrorInfosManager::LogErrorInfo(DebuggerErrLevel level, DebuggerErrno errId, const std::string& info)
{
    if (level < threshold) {
        return;
    }

    std::lock_guard<std::mutex> lk(g_errInfoMtx);
    std::ostream& output = std::cout;
    output << "[" << ErrorLevelString[level] << "]";
    if (errId != DebuggerErrno::NONE) {
        output << "[" << ErrnoString[errId] << "]";
    }
    output << ToSafeString(info) << std::endl;

    if (level > topLevel) {
        topLevel = level;
    }

    return;
}

DebuggerErrLevel ErrorInfosManager::GetTopErrLevelInDuration()
{
    std::lock_guard<std::mutex> lk(g_errInfoMtx);
    DebuggerErrLevel ret = topLevel;
    topLevel = DebuggerErrLevel::LEVEL_NONE;
    return ret;
}

__attribute__((constructor)) void InitDebuggerThreshold()
{
    const char* msprobeLogLevelEnv = getenv("MSPROBE_LOG_LEVEL");
    if (msprobeLogLevelEnv == nullptr) {
        return;
    }

    int msprobeLogLevel = 1;
    try {
        msprobeLogLevel = std::stoi(msprobeLogLevelEnv);
    } catch (const std::exception& e) {
        return;
    }

    if (msprobeLogLevel >= static_cast<int>(DebuggerErrLevel::LEVEL_DEBUG) &&
        msprobeLogLevel <= static_cast<int>(DebuggerErrLevel::LEVEL_CRITICAL)) {
        ErrorInfosManager::SetLogThreshold(static_cast<DebuggerErrLevel>(msprobeLogLevel));
    }
}

}