/* -------------------------------------------------------------------------
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * This file is part of the MindStudio project.
 *
 * 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 "analysis/csrc/domain/data_process/system/host_usage_processor.h"
#include "analysis/csrc/domain/services/environment/context.h"

namespace Analysis {
namespace Domain {
using namespace Analysis::Domain::Environment;
HostUsageProcessor::HostUsageProcessor(const std::string &profPath_, const std::string &processorName, DBInfo &&dbInfo)
    : usageDb_(dbInfo), processorName_(processorName), DataProcessor(profPath_) {}

bool HostUsageProcessor::Process(DataInventory &dataInventory)
{
    INFO("Start process %.", processorName_);
    ProfTimeRecord record;
    if (!Context::GetInstance().GetProfTimeRecordInfo(record, profPath_)) {
        ERROR("Failed to obtain the time in start_info and end_info.");
        return false;
    }
    std::string dbPath = Utils::File::PathJoin({profPath_, HOST, SQLITE, usageDb_.dbName});
    if (!usageDb_.ConstructDBRunner(dbPath) || usageDb_.dbRunner == nullptr) {
        return false;
    }
    auto status = CheckPathAndTable(dbPath, usageDb_);
    if (status == CHECK_FAILED) {
        return false;
    } else if (status == NOT_EXIST) {
        return true;
    }
    return ProcessData(dataInventory, record, dbPath);
}

HostCpuUsageProcessor::HostCpuUsageProcessor(const std::string &profPath_)
    : HostUsageProcessor(profPath_, PROCESSOR_NAME_CPU_USAGE, DBInfo("host_cpu_usage.db", "CpuUsage")) {}

bool HostCpuUsageProcessor::ProcessData(DataInventory &dataInventory, const ProfTimeRecord &record,
                                        const std::string &dbPath)
{
    OriCpuUsage oriData;
    std::vector<CpuUsageData> res;
    std::string sql{"SELECT start_time, cpu_no, usage FROM " + usageDb_.tableName +
                    " ORDER BY CAST(cpu_no AS DECIMAL)"};
    if (!usageDb_.dbRunner->QueryData(sql, oriData)) {
        ERROR("Query cpu usage data failed, db path is %.", dbPath);
    }
    if (oriData.empty()) {
        ERROR("cpu usage original data is empty. db path is %", dbPath);
        return false;
    }
    if (!Reserve(res, oriData.size())) {
        ERROR("Reserved for cpu usage data failed.");
        return false;
    }
    CpuUsageData data;
    for (const auto &row : oriData) {
        std::tie(data.timestamp, data.cpuNo, data.usage) = row;
        HPFloat startTimestamp{data.timestamp};
        data.timestamp = GetLocalTime(startTimestamp, record).Uint64();
        res.push_back(data);
    }
    FilterDataByStartTime(res, record.startTimeNs, processorName_);
    if (!SaveToDataInventory<CpuUsageData>(std::move(res), dataInventory, processorName_)) {
        ERROR("Save data failed, %.", processorName_);
        return false;
    }
    return true;
}

HostMemUsageProcessor::HostMemUsageProcessor(const std::string &profPath_)
    : HostUsageProcessor(profPath_, PROCESSOR_NAME_MEM_USAGE, DBInfo("host_mem_usage.db", "MemUsage")) {}

bool HostMemUsageProcessor::ProcessData(DataInventory &dataInventory, const ProfTimeRecord &record,
                                        const std::string &dbPath)
{
    OriMemUsage oriData;
    std::vector<MemUsageData> res;
    std::string sql{"SELECT start_time, usage FROM " + usageDb_.tableName};
    if (!usageDb_.dbRunner->QueryData(sql, oriData)) {
        ERROR("Query mem usage data failed, db path is %.", dbPath);
    }
    if (oriData.empty()) {
        ERROR("mem usage original data is empty. db path is %", dbPath);
        return false;
    }
    if (!Reserve(res, oriData.size())) {
        ERROR("Reserved for mem usage data failed.");
        return false;
    }
    MemUsageData data;
    for (const auto &row : oriData) {
        std::tie(data.timestamp, data.usage) = row;
        HPFloat startTimestamp{data.timestamp};
        data.timestamp = GetLocalTime(startTimestamp, record).Uint64();
        res.push_back(data);
    }
    FilterDataByStartTime(res, record.startTimeNs, processorName_);
    if (!SaveToDataInventory<MemUsageData>(std::move(res), dataInventory, processorName_)) {
        ERROR("Save data failed, %.", processorName_);
        return false;
    }
    return true;
}

HostDiskUsageProcessor::HostDiskUsageProcessor(const std::string &profPath_)
    : HostUsageProcessor(profPath_, PROCESSOR_NAME_DISK_USAGE, DBInfo("host_disk_usage.db", "DiskUsage")) {}

bool HostDiskUsageProcessor::ProcessData(DataInventory &dataInventory, const ProfTimeRecord &record,
                                         const std::string &dbPath)
{
    OriDiskUsage oriData;
    std::vector<DiskUsageData> res;
    std::string sql{"SELECT start_time, usage, disk_read, disk_write FROM " + usageDb_.tableName};
    if (!usageDb_.dbRunner->QueryData(sql, oriData)) {
        ERROR("Query disk usage data failed, db path is %.", dbPath);
    }
    if (oriData.empty()) {
        ERROR("disk usage original data is empty. db path is %", dbPath);
        return false;
    }
    if (!Reserve(res, oriData.size())) {
        ERROR("Reserved for disk usage data failed.");
        return false;
    }
    DiskUsageData data;
    for (const auto &row : oriData) {
        std::tie(data.timestamp, data.usage, data.readRate, data.writeRate) = row;
        HPFloat startTimestamp{data.timestamp};
        data.timestamp = GetLocalTime(startTimestamp, record).Uint64();
        res.push_back(data);
    }
    FilterDataByStartTime(res, record.startTimeNs, processorName_);
    if (!SaveToDataInventory<DiskUsageData>(std::move(res), dataInventory, processorName_)) {
        ERROR("Save data failed, %.", processorName_);
        return false;
    }
    return true;
}

HostNetworkUsageProcessor::HostNetworkUsageProcessor(const std::string &profPath_)
    : HostUsageProcessor(profPath_, PROCESSOR_NAME_NETWORK_USAGE, DBInfo("host_network_usage.db", "NetworkUsage")) {}

bool HostNetworkUsageProcessor::ProcessData(DataInventory &dataInventory, const ProfTimeRecord &record,
                                            const std::string &dbPath)
{
    OriNetworkUsage oriData;
    std::vector<NetWorkUsageData> res;
    std::string sql{"SELECT start_time, usage, speed FROM " + usageDb_.tableName};
    if (!usageDb_.dbRunner->QueryData(sql, oriData)) {
        ERROR("Query network usage data failed, db path is %.", dbPath);
    }
    if (oriData.empty()) {
        ERROR("network usage original data is empty. db path is %", dbPath);
        return false;
    }
    if (!Reserve(res, oriData.size())) {
        ERROR("Reserved for network usage data failed.");
        return false;
    }
    NetWorkUsageData data;
    for (const auto &row : oriData) {
        std::tie(data.timestamp, data.usage, data.speed) = row;
        HPFloat startTimestamp{data.timestamp};
        data.timestamp = GetLocalTime(startTimestamp, record).Uint64();
        res.push_back(data);
    }
    FilterDataByStartTime(res, record.startTimeNs, processorName_);
    if (!SaveToDataInventory<NetWorkUsageData>(std::move(res), dataInventory, processorName_)) {
        ERROR("Save data failed, %.", processorName_);
        return false;
    }
    return true;
}

OSRuntimeApiProcessor::OSRuntimeApiProcessor(const std::string &profPath_)
    : HostUsageProcessor(profPath_, PROCESSOR_NAME_OSRT_API, DBInfo("host_runtime_api.db", "Syscall")) {}

bool OSRuntimeApiProcessor::ProcessData(DataInventory &dataInventory, const ProfTimeRecord &record,
                                        const std::string &dbPath)
{
    OriSysCall oriData;
    std::vector<OSRuntimeApiData> res;
    std::string sql{"SELECT runtime_api_name, runtime_pid, runtime_tid, runtime_trans_start, runtime_trans_end "
                    "FROM " + usageDb_.tableName};
    if (!usageDb_.dbRunner->QueryData(sql, oriData)) {
        ERROR("Query os runtime api data failed, db path is %.", dbPath);
    }
    if (oriData.empty()) {
        ERROR("os runtime api original data is empty. db path is %", dbPath);
        return false;
    }
    if (!Reserve(res, oriData.size())) {
        ERROR("Reserved for runtime api failed.");
        return false;
    }
    OSRuntimeApiData data;
    for (const auto &row : oriData) {
        std::tie(data.name, data.pid, data.tid, data.timestamp, data.endTime) = row;
        HPFloat startTimestamp{data.timestamp};
        data.timestamp = GetLocalTime(startTimestamp, record).Uint64();
        HPFloat endTimestamp{data.endTime};
        data.endTime = GetLocalTime(endTimestamp, record).Uint64();
        res.push_back(data);
    }
    FilterDataByStartTime(res, record.startTimeNs, processorName_);
    if (!SaveToDataInventory<OSRuntimeApiData>(std::move(res), dataInventory, processorName_)) {
        ERROR("Save data failed, %.", processorName_);
        return false;
    }
    return true;
}
}
}