/*
 * Copyright (c) 2026 Huawei Technologies Co., Ltd.
 * openUBMC 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 <cstdlib>
#include <devmon-common/global_model.h>
#include <mc/filesystem.h>
#include <mc/json.h>
#include <mc/log.h>

namespace devmon {

global_model& global_model::get_instance()
{
    static global_model instance;
    return instance;
}

void global_model::init()
{
    m_class_info.clear();
    std::string path = find_config_path();
    if (!mc::filesystem::exists(path)) {
        wlog("${path} does not exist, skip register global object", ("path", path));
        hw_stream_warn("${path} does not exist, skip register global object", ("path", path));
        return;
    }
    try {
        parse_config(path);
    } catch (const std::exception& e) {
        elog("parse global object info failed, err: ${err}", ("err", e.what()));
    }
}

std::optional<instance_info_t> global_model::get_object_info(const std::string& object_name,
                                                             const std::string& prop_name)
{
    std::string interface = "bmc.kepler." + object_name;
    if (m_class_info.contains(interface) && m_class_info[interface].contains(prop_name)) {
        instance_info_t info;
        info.path      = m_class_info["path"].as<std::string>();
        info.app       = "persistence";
        info.interface = interface;
        return info;
    }
    return std::nullopt;
}

std::string global_model::find_config_path()
{
#if defined(BUILD_TYPE) && defined(BUILD_TYPE_DT) && BUILD_TYPE == BUILD_TYPE_DT
    const char* env_path = std::getenv("GLOBAL_JSON_PATH");
    std::string path     = env_path ? env_path : GLOBAL_MODEL_DEFAULT_PATH;
    if (env_path) {
        nlog("try to load ${path} to register global object", ("path", path.substr(path.rfind('/') + 1)));
    }
    return path;
#else
    std::optional<std::pair<std::string, std::string>> info = utils::get_product_info();
    if (!info.has_value()) {
        nlog("get product id failed, try to load global.json to register global object");
        return GLOBAL_MODEL_DEFAULT_PATH;
    }
    std::string path = "/opt/bmc/conf/global/global_" + info->first + "_" + info->second + ".json";
    if (!mc::filesystem::exists(path)) {
        nlog("${path} does not exist, try to load global.json to register global object",
             ("path", path.substr(path.rfind('/') + 1)));
        return GLOBAL_MODEL_DEFAULT_PATH;
    }
    nlog("try to load ${path} to register global object", ("path", path.substr(path.rfind('/') + 1)));
    return path;
#endif
}

void global_model::parse_config(const std::string& path)
{
    auto content = mc::filesystem::read_file(path);
    if (!content.has_value()) {
        wlog("Failed to read global.json");
        return;
    }
    mc::dict model_json;
    try {
        model_json = mc::json::json_decode(*content).as_dict();
    } catch (const std::exception& e) {
        elog("get global object json data failed, err: ${err}", ("err", e.what()));
        return;
    }
    auto& global_data    = model_json["Global"];
    m_class_info["path"] = global_data["path"];
    for (auto& entry : global_data["interfaces"].as_dict()) {
        auto     value = entry.value;
        mc::dict info;
        for (auto& sub_entry : value["properties"].as_dict()) {
            info[sub_entry.key] = true;
        }
        m_class_info[entry.key] = info;
    }
}

} // namespace devmon