/*
 * Copyright (c) 2021-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 <cstdint>
#include <ctime>
#include <mutex>
#include <string>
#include <sys/stat.h>
#include <vector>

#include "constants.h"
#include "faultlog_info.h"
#include "string_util.h"
#include "time_util.h"

namespace OHOS {
namespace HiviewDFX {
namespace {
constexpr int DEFAULT_BUFFER_SIZE = 64;
} // namespace

std::string GetFormatedTime(uint64_t target)
{
    time_t now = time(nullptr);
    if (target > static_cast<uint64_t>(now)) {
        target = target / 1000; // 1000 : convert millisecond to seconds
    }

    time_t out = static_cast<time_t>(target);
    struct tm tmStruct {0};
    struct tm* timeInfo = localtime_r(&out, &tmStruct);
    if (timeInfo == nullptr) {
        return "00000000000000";
    }

    char buf[DEFAULT_BUFFER_SIZE] = {0};
    strftime(buf, DEFAULT_BUFFER_SIZE - 1, "%Y%m%d%H%M%S", timeInfo);
    return std::string(buf, strlen(buf));
}

std::string GetFaultNameByType(int32_t faultType, bool asFileName)
{
    switch (faultType) {
        case FaultLogType::JS_CRASH:
            return asFileName ? "jscrash" : "JS_ERROR";
        case FaultLogType::CPP_CRASH:
            return asFileName ? "cppcrash" : "CPP_CRASH";
        case FaultLogType::APP_FREEZE:
            return asFileName ? "appfreeze" : "APP_FREEZE";
        case FaultLogType::SYS_FREEZE:
            return asFileName ? "sysfreeze" : "SYS_FREEZE";
        case FaultLogType::SYS_WARNING:
            return asFileName ? "syswarning" : "SYS_WARNING";
        case FaultLogType::RUST_PANIC:
            return asFileName ? "rustpanic" : "RUST_PANIC";
        case FaultLogType::ADDR_SANITIZER:
            return asFileName ? "sanitizer" : "ADDR_SANITIZER";
        default:
            break;
    }
    return "Unknown";
}

std::string GetFaultLogName(const FaultLogInfo& info)
{
    std::string name = info.module;
    if (name.find("/") != std::string::npos) {
        name = info.module.substr(info.module.find_last_of("/") + 1);
    }

    std::string ret = "";
    if (info.faultLogType == FaultLogType::ADDR_SANITIZER) {
        if (info.reason.compare("TSAN") == 0) {
            ret.append("tsan");
        } else if (info.reason.compare("UBSAN") == 0) {
            ret.append("ubsan");
        } else if (info.reason.compare("GWP-ASAN") == 0) {
            ret.append("gwpasan");
        } else if (info.reason.find("HWASAN") != std::string::npos) {
            ret.append("hwasan");
        } else if (info.reason.find("ASAN") != std::string::npos) {
            ret.append("asan");
        } else {
            ret.append("sanitizer");
        }
    } else {
        ret.append(GetFaultNameByType(info.faultLogType, true));
    }
    ret.append("-");
    ret.append(name);
    ret.append("-");
    ret.append(std::to_string(info.id));
    ret.append("-");
    ret.append(GetFormatedTime(info.time));
    return ret;
}

int32_t GetLogTypeByName(const std::string& type)
{
    if (type == "jscrash") {
        return FaultLogType::JS_CRASH;
    } else if (type == "cppcrash") {
        return FaultLogType::CPP_CRASH;
    } else if (type == "appfreeze") {
        return FaultLogType::APP_FREEZE;
    } else if (type == "sysfreeze") {
        return FaultLogType::SYS_FREEZE;
    } else if (type == "syswarning") {
        return FaultLogType::SYS_WARNING;
    } else if (type == "sanitizer") {
        return FaultLogType::ADDR_SANITIZER;
    } else if (type == "all" || type == "ALL") {
        return FaultLogType::ALL;
    } else {
        return -1;
    }
}

FaultLogInfo ExtractInfoFromFileName(const std::string& fileName)
{
    // FileName LogType-PackageName-Uid-YYYYMMDDHHMMSS
    FaultLogInfo info;
    std::vector<std::string> splitStr;
    const int32_t expectedVecSize = 4;
    StringUtil::SplitStr(fileName, "-", splitStr);
    if (splitStr.size() == expectedVecSize) {
        info.faultLogType = GetLogTypeByName(splitStr[0]);                 // 0 : index of log type
        info.module = splitStr[1];                                         // 1 : index of module name
        StringUtil::ConvertStringTo<int32_t>(splitStr[2], info.id);        // 2 : index of uid
        info.time = TimeUtil::StrToTimeStamp(splitStr[3], "%Y%m%d%H%M%S"); // 3 : index of timestamp
    }
    info.pid = 0;
    return info;
}

FaultLogInfo ExtractInfoFromTempFile(const std::string& fileName)
{
    // FileName LogType-pid-time
    FaultLogInfo info;
    std::vector<std::string> splitStr;
    const int32_t expectedVecSize = 3;
    StringUtil::SplitStr(fileName, "-", splitStr);
    if (splitStr.size() == expectedVecSize) {
        info.faultLogType = GetLogTypeByName(splitStr[0]);                 // 0 : index of log type
        StringUtil::ConvertStringTo<int32_t>(splitStr[1], info.pid);       // 1 : index of pid
        StringUtil::ConvertStringTo<int64_t>(splitStr[2], info.time);      // 2 : index of timestamp
    }
    return info;
}

std::string RegulateModuleNameIfNeed(const std::string& name)
{
    std::vector<std::string> splitStr;
    StringUtil::SplitStr(name, "/", splitStr);
    auto size = splitStr.size();
    if (size > 0) {
        return splitStr[size - 1];
    }
    return name;
}

time_t GetFileLastAccessTimeStamp(const std::string& fileName)
{
    struct stat fileInfo;
    if (stat(fileName.c_str(), &fileInfo) != 0) {
        return 0;
    }
    return fileInfo.st_atime;
}

std::string GetCppCrashTempLogName(const FaultLogInfo& info)
{
    return std::string(FaultLogger::DEFAULT_FAULTLOG_TEMP_FOLDER) +
        "cppcrash-" +
        std::to_string(info.pid) +
        "-" +
        std::to_string(info.time);
}

std::string GetThreadStack(const std::string& path, int32_t threadId)
{
    std::string stack;
    if (path.empty()) {
        return stack;
    }
    char realPath[PATH_MAX] = {0};
    if (realpath(path.c_str(), realPath) == nullptr) {
        return stack;
    }
    if (strncmp(realPath, FaultLogger::FAULTLOG_BASE_FOLDER, strlen(FaultLogger::FAULTLOG_BASE_FOLDER)) != 0) {
        return stack;
    }

    std::ifstream logFile(realPath);
    if (!logFile.is_open()) {
        return stack;
    }
    std::string regTidString = "^Tid:" + std::to_string(threadId) + ", Name:(.{0,32})$";
    std::regex regTid(regTidString);
    std::regex regStack(R"(^#\d{2,3} (pc|at) .{0,1024}$)");
    std::string line;
    while (std::getline(logFile, line)) {
        if (!logFile.good()) {
            break;
        }

        if (!std::regex_match(line, regTid)) {
            continue;
        }

        do {
            stack.append(line + "\n");
            if (!logFile.good()) {
                break;
            }
        } while (std::getline(logFile, line) && std::regex_match(line, regStack));
        break;
    }

    return stack;
}
} // namespace HiviewDFX
} // namespace OHOS