/*
 * Copyright 2024 KylinSoft Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */

#include "modelconfigmanager.h"

#include <jsoncpp/json/json.h>
#include <kylin-ai/ai-engine/imageprocessengine.h>

#include <chrono>

#include "encryption/encryption.h"
#include "utils/logger.h"
#include "utils/util.h"

namespace {
const char *publicCloudSchemaId = "com.kylin.aisdk.model.cloud";
const char *customCloudSchemaId = "com.kylin.aisdk.model.custom";
const char *ondeviceSchemaId = "com.kylin.aisdk.model.ondevice";

const std::string oldModelConfigFile = std::string(getenv("HOME")) + "/.config/model_manager.json";

const std::string baiduNlpModelName = "百度-ERNIE-Bot-4";
const std::string baiduVisionModelName = "百度-文心AI作画高级版";

thread_local bool isModelInfosLoaderThread = false;

ModelInfo createModelInfo(const Json::Value &json, AiCapability capability, DeployType deployType,
                          const std::string &engineName, const std::string &vendor) {
    if (!json.isMember("name")) {
        LOGE("Invalid configs format: {}", json.toStyledString());
        return {};
    }
    const std::string modelName = json["name"].asString();
    const std::string model = json["model"].asString();

    if (!json.isMember("subConfig") || !json["subConfig"].isArray()) {
        LOGE("Invalid configs format: {}", json.toStyledString());
        return {};
    }

    std::vector<MultiAuthentication> multiAuthentications;
    for (const auto &config : json["subConfig"]) {
        if (!config.isMember("name") || !config.isMember("config") || !config["config"].isArray()) {
            LOGE("Invalid configs format: {}", json.toStyledString());
            return {};
        }

        const std::string itemName = config["name"].asString();
        const auto configs = config["config"];
        std::vector<Authentication> authentications;
        for (const auto &config : configs) {
            if (!config.isMember("friendlyName") || !config["configName"].isString()) {
                LOGE("Invalid configs format: {}", json.toStyledString());
                return {};
            }
            authentications.push_back({config["friendlyName"].asString(), config["configName"].asString(), ""});
        }
        multiAuthentications.push_back({itemName, authentications});
    }

    const std::string debName = json["debName"].asString();

    const bool is3rdModel = (json["is3rdModel"].asString() == "true");

    const int maxTokens = json["maxTokens"].asInt();

    return {{modelName, model, "", vendor, debName, is3rdModel, capability, deployType, engineName},
            multiAuthentications};
}
std::vector<ModelInfo> createModelInfosFromJsonString(const std::string &jsonString, AiCapability capability,
                                                      DeployType deployType, const std::string &engineName) {
    std::vector<ModelInfo> modelInfos;

    auto root = util::stringToJson(jsonString);
    if (!root.isObject()) {
        LOGE("Invalid configs format: {}", jsonString);
        return {};
    }

    if (!root.isMember("vendor") || !root.isMember("models") || !root["models"].isArray()) {
        LOGE("Invalid configs format: {}", jsonString);
        return {};
    }

    const std::string vendor = root["vendor"].asString();
    const auto models = root["models"];

    for (const auto &model : models) {
        modelInfos.push_back(createModelInfo(model, capability, deployType, engineName, vendor));
    }

    return modelInfos;
}

std::vector<ModelInfo> createCustomModelInfosFromJsonString(const std::string &jsonString,
                                                            const std::string &engineName) {
    std::vector<ModelInfo> modelInfos;

    auto root = util::stringToJson(jsonString);
    if (!root.isObject()) {
        LOGE("Invalid configs format: {}", jsonString);
        return {};
    }

    const Json::Value::Members modelNames = root.getMemberNames();
    for (const auto &modelName : modelNames) {
        if (modelName == "currentModel") {
            continue;
        }

        const auto modelConfig = root[modelName];
        if (!modelConfig.isObject()) {
            LOGE("Invalid configs format for: {}", modelName);
            continue;
        }

        if (!modelConfig.isMember("apiUrl")) {
            LOGE("Invalid configs format: {}", modelConfig.toStyledString());
            continue;
        }

        Model model{modelName,
                    modelConfig["model"].asString(),
                    modelConfig["apiUrl"].asString(),
                    "",
                    "",
                    false,
                    AiCapability::Nlp,
                    DeployType::Custom,
                    engineName};
        std::vector<MultiAuthentication> multiAuthentications{
            {"", {{"APIKey", "apiKey", modelConfig["apiKey"].asString()}}},
        };

        modelInfos.push_back({model, multiAuthentications});
    }

    return modelInfos;
}

Json::Value authenticationsToJsonWithEncryption(const std::vector<MultiAuthentication> &authentications) {
    Json::Value root;
    for (const auto &auth : authentications) {
        Json::Value item;
        item["itemName"] = auth.itemName;
        for (const auto &auth : auth.authentications) {
            Json::Value authenDetails;
            authenDetails["friendlyKeyName"] = auth.friendlyKeyName;
            authenDetails["key"] = auth.key;
            authenDetails["value"] = Encryption::getInstance().encrypt(auth.value);
            item["authentications"].append(authenDetails);
        }
        root.append(item);
    }
    return root;
}

std::vector<MultiAuthentication> jsonToAuthenticationsWithDecryption(const Json::Value &root) {
    std::vector<MultiAuthentication> authentications;
    for (const auto &item : root) {
        MultiAuthentication multiAuth;
        multiAuth.itemName = item["itemName"].asString();
        const auto authens = item["authentications"];
        for (const auto &authDetails : authens) {
            Authentication auth;
            auth.friendlyKeyName = authDetails["friendlyKeyName"].asString();
            auth.key = authDetails["key"].asString();
            auth.value = Encryption::getInstance().decrypt(authDetails["value"].asString());
            multiAuth.authentications.push_back(auth);
        }
        authentications.push_back(multiAuth);
    }
    return authentications;
}

std::vector<MultiAuthentication> customModelJsonToAuthenticationsWithDecryption(const Json::Value &root) {
    if (!root.isMember("apiKey") || !root["apiKey"].isString()) {
        return {};
    }

    const std::string apiKey = root["apiKey"].asString();
    return {{"", {{"APIKey", "apiKey", Encryption::getInstance().decrypt(apiKey)}}}};
}

std::string aiCapabilityString(AiCapability capability) {
    switch (capability) {
        case AiCapability::Nlp:
            return "nlp";
        case AiCapability::Speech:
            return "speech";
        case AiCapability::Vision:
            return "vision";
        default:
            return "";
    }
}

Json::Value authensToEngineConfigJson(const std::vector<MultiAuthentication> &multiAuthens) {
    Json::Value root;
    for (const auto &multiAuth : multiAuthens) {
        for (const auto &auth : multiAuth.authentications) {
            root[auth.key] = auth.value;
        }
    }
    return root;
}

}  // namespace

ModelConfigManager &ModelConfigManager::getInstance() {
    static ModelConfigManager instance(ai_engine::AiEnginePluginManager::getInstance());
    return instance;
}

ModelConfigManager::ModelConfigManager(ai_engine::AiEnginePluginManager &aiEnginePluginManager)
    : aiEnginePluginManager_(aiEnginePluginManager),
      cloudSettings_(publicCloudSchemaId),
      customSettings_(customCloudSchemaId),
      ondeviceSettings_(ondeviceSchemaId) {
    modelInfosLoadFuture_ = std::async(std::launch::async, [this]() {
        isModelInfosLoaderThread = true;
        loadModelInfos();
        if (needToMigrateOldModelConfig(baiduNlpModelName)) {
            migrateOldModelConfig(baiduNlpModelName, AiCapability::Nlp);
        }
        if (needToMigrateOldModelConfig(baiduVisionModelName)) {
            migrateOldModelConfig(baiduVisionModelName, AiCapability::Vision);
        }
        setDefaultCloudNlpModelIfNotCurrentSet();
        isModelInfosLoaderThread = false;
    });
}

std::vector<Model> ModelConfigManager::getModels(AiCapability capability) const {
    waitForModelInfosLoaded();

    std::vector<Model> models;

    std::lock_guard<std::mutex> lock(modelInfosMutex_);
    for (const auto &modelInfo : modelInfos_) {
        if (modelInfo.model.capability == capability) {
            // 暂时不显示端侧视觉模型
            if (modelInfo.model.capability == AiCapability::Vision && modelInfo.model.deployType == DeployType::Local) {
                continue;
            }
            models.push_back(modelInfo.model);
        }
    }
    return models;
}

std::vector<Model> ModelConfigManager::getModels(DeployType deployType) const {
    waitForModelInfosLoaded();

    std::vector<Model> models;
    std::lock_guard<std::mutex> lock(modelInfosMutex_);
    for (const auto &modelInfo : modelInfos_) {
        if (modelInfo.model.deployType == deployType) {
            models.push_back(modelInfo.model);
        }
    }
    return models;
}

std::string ModelConfigManager::getModelVersionByModelName(const std::string &modelName) const {
    waitForModelInfosLoaded();

    std::lock_guard<std::mutex> lock(modelInfosMutex_);
    for (const auto &modelInfo : modelInfos_) {
        if (modelInfo.model.modelName == modelName) {
            return modelInfo.model.modelVersion;
        }
    }
    for (const auto &trialModelInfo : freeTrialModelInfos_) {
        if (trialModelInfo.model.modelName == modelName) {
            return trialModelInfo.model.modelVersion;
        }
    }
    return {};
}

std::string ModelConfigManager::getCurrentModelName(AiCapability capability, DeployType deployType) const {
    waitForModelInfosLoaded();
    auto doGetCurrentModelName = [&](const Settings &settings, AiCapability capability) {
        const std::string jsonString = settings.getJsonValue(aiCapabilityString(capability));
        const Json::Value jsonConfig = util::stringToJson(jsonString);
        if (jsonConfig.isMember("currentModel")) {
            LOGD(".isMember(currentModel)  {} ", jsonConfig["currentModel"].asString());
        }
        return jsonConfig.isMember("currentModel") ? jsonConfig["currentModel"].asString() : "";
    };

    switch (deployType) {
        case DeployType::Local: {
            return doGetCurrentModelName(ondeviceSettings_, capability);
        }

        case DeployType::Custom: {
            return doGetCurrentModelName(customSettings_, capability);
        }

        case DeployType::PublicCloud: {
            return doGetCurrentModelName(cloudSettings_, capability);
        }
        default: {
            return "";
        }
    }
    return "";
}

std::vector<MultiAuthentication> ModelConfigManager::getModelAuthentications(const std::string &modelName) const {
    waitForModelInfosLoaded();

    if (!containsModel(modelName)) {
        return {};
    }

    auto modelInfo = getModelInfo(modelName);

    if (modelInfo.model.deployType == DeployType::Local) {
        return {};
    }

    const std::string capabilityKey = aiCapabilityString(modelInfo.model.capability);

    auto &settings = modelInfo.model.deployType == DeployType::Custom ? customSettings_ : cloudSettings_;

    const std::string capabilityJsonString = settings.getJsonValue(capabilityKey);
    Json::Value jsonConfig = util::stringToJson(capabilityJsonString);
    if (!jsonConfig.isMember(modelName)) {
        return modelInfo.multiAuthentications;
    }

    Json::Value jsonAuths = jsonConfig[modelName];

    return modelInfo.model.deployType == DeployType::Custom ? customModelJsonToAuthenticationsWithDecryption(jsonAuths)
                                                            : jsonToAuthenticationsWithDecryption(jsonAuths);
}

bool ModelConfigManager::getModelDeployEnabled(DeployType deployType) const {
    switch (deployType) {
        case DeployType::Local: {
            return ondeviceSettings_.getBoolValue("enabled");
        }
        case DeployType::Custom: {
            return customSettings_.getBoolValue("enabled");
        }
        case DeployType::PublicCloud: {
            return cloudSettings_.getBoolValue("enabled");
        }
        default: {
            return false;
        }
    }
    return false;
}

bool ModelConfigManager::setCurrentModelName(AiCapability capability, DeployType deployType,
                                             const std::string &modelName) {
    waitForModelInfosLoaded();

    if (!containsModel(modelName)) {
        LOGE("Model not found: {}", modelName);
        return false;
    }

    modelChangedCallbacks_.callAllCallbacks(capability, deployType);

    auto doSetCurrentModelName = [modelName](Settings &settings, AiCapability capability) {
        const std::string jsonString = settings.getJsonValue(aiCapabilityString(capability));
        Json::Value jsonConfig = util::stringToJson(jsonString);
        jsonConfig["currentModel"] = modelName;
        const std::string newJsonString = jsonConfig.toStyledString();
        settings.setJsonValue(aiCapabilityString(capability), newJsonString);
    };

    switch (deployType) {
        case DeployType::Local: {
            doSetCurrentModelName(ondeviceSettings_, capability);
            return true;
        }
        case DeployType::Custom: {
            doSetCurrentModelName(customSettings_, capability);
            return true;
        }
        case DeployType::PublicCloud: {
            doSetCurrentModelName(cloudSettings_, capability);
            return true;
        }
        default:
            return false;
    }
    return false;
}

/// !todo 按照authentications中指定的itemName进行更新
bool ModelConfigManager::setModelAuthentications(const std::string &modelName,
                                                 const std::vector<MultiAuthentication> &authentications) {
    if (authentications.empty()) {
        LOGE("Authentications is empty.");
        return false;
    }

    waitForModelInfosLoaded();

    if (!containsModel(modelName)) {
        LOGE("Model not found: {}.", modelName);
        return false;
    }

    auto modelInfo = getModelInfo(modelName);
    if (modelInfo.model.deployType == DeployType::Local) {
        return false;
    }

    auto &settings = modelInfo.model.deployType == DeployType::Custom ? customSettings_ : cloudSettings_;

    updateModelAuthenticationsSettings(settings, modelInfo, authentications);
    updateModelAuthenticationsInfo(modelName, authentications);

    modelChangedCallbacks_.callAllCallbacks(modelInfo.model.capability, modelInfo.model.deployType);

    return true;
}

void ModelConfigManager::updateModelAuthenticationsSettings(Settings &settings, const ModelInfo &modelInfo,
                                                            const std::vector<MultiAuthentication> &authentications) {
    const std::string capabilityKey = aiCapabilityString(modelInfo.model.capability);

    // 对于custom类型返回的是字符串，其他类型返回Json::Value
    auto jsonAuths = modelInfo.model.deployType == DeployType::Custom
                         ? Encryption::getInstance().encrypt(
                               authentications.empty() ? "" : authentications[0].authentications[0].value)
                         : authenticationsToJsonWithEncryption(authentications);

    const std::string capabilityJsonString = settings.getJsonValue(capabilityKey);
    Json::Value jsonConfig = util::stringToJson(capabilityJsonString);

    auto &keyObj = modelInfo.model.deployType == DeployType::Custom ? jsonConfig[modelInfo.model.modelName]["apiKey"]
                                                                    : jsonConfig[modelInfo.model.modelName];
    LOGD("Update model authentications settings: {}.", keyObj.toStyledString());
    keyObj = jsonAuths;
    const std::string jsonString = jsonConfig.toStyledString();
    LOGD("Update model authentications settings: {}.", jsonConfig.toStyledString());
    settings.setJsonValue(capabilityKey, jsonString);
}

void ModelConfigManager::updateModelAuthenticationsInfo(const std::string &modelName,
                                                        const std::vector<MultiAuthentication> &authentications) {
    std::lock_guard<std::mutex> lock(modelInfosMutex_);
    for (auto &model : modelInfos_) {
        LOGD("{} {}", model.model.modelName, modelName);
        if (model.model.modelName == modelName) {
            model.multiAuthentications = authentications;
            break;
        }
    }
}

void ModelConfigManager::clearModelAuthenticationsSettings(Settings &settings, const ModelInfo &modelInfo) {
    const std::string capabilityKey = aiCapabilityString(modelInfo.model.capability);
    const std::string capabilityJsonString = settings.getJsonValue(capabilityKey);
    LOGD("{}", capabilityJsonString);
    Json::Value jsonConfig = util::stringToJson(capabilityJsonString);

    if (modelInfo.model.deployType == DeployType::Custom) {
        jsonConfig[modelInfo.model.modelName]["apiKey"] = "";
        settings.setJsonValue(capabilityKey, jsonConfig.toStyledString());
        return;
    }

    if (jsonConfig.isMember(modelInfo.model.modelName)) {
        auto &authentications = jsonConfig[modelInfo.model.modelName];
        if (authentications.isArray()) {
            for (auto &authentication : authentications) {
                for (auto &auth : authentication["authentications"]) {
                    auth["value"] = "";
                }
            }
        }
        if (jsonConfig["currentModel"].asString() == modelInfo.model.modelName) {
            jsonConfig.removeMember("currentModel");
        }
        std::string jsonString = jsonConfig.toStyledString();
        LOGD("jsonString {}", jsonString);
        settings.setJsonValue(capabilityKey, jsonString);
    }
}

void ModelConfigManager::clearModelAuthenticationsInfo(const std::string &modelName) {
    std::lock_guard<std::mutex> lock(modelInfosMutex_);
    for (auto &model : modelInfos_) {
        if (model.model.modelName == modelName) {
            model.multiAuthentications.clear();
            break;
        }
    }
}

void ModelConfigManager::setDefaultCloudNlpModelIfNotCurrentSet() {
    if (!getCurrentModelName(AiCapability::Nlp, DeployType::PublicCloud).empty()) {
        return;
    }
    for (auto modelInfo : freeTrialModelInfos_) {
        if (setCurrentModelName(AiCapability::Nlp, DeployType::PublicCloud, modelInfo.model.modelName)) {
            break;
        }
    }
}

// 对于云端模型来说是删除所有的模型配置
// 对于自定义模型仅仅是清除apiKey的内容
/// !todo 具体逻辑需要进一步优化
bool ModelConfigManager::clearModelAuthentications(const std::string &modelName) {
    waitForModelInfosLoaded();

    if (!containsModel(modelName)) {
        LOGE("Model not found: {}", modelName);
        return false;
    }

    ModelInfo modelInfo = getModelInfo(modelName);
    auto &settings = modelInfo.model.deployType == DeployType::Custom ? customSettings_ : cloudSettings_;

    clearModelAuthenticationsSettings(settings, modelInfo);
    clearModelAuthenticationsInfo(modelName);

    modelChangedCallbacks_.callAllCallbacks(modelInfo.model.capability, modelInfo.model.deployType);

    return true;
}

bool ModelConfigManager::setModelSchedulingPriority(DeployType deployType, int priority) {
    modelChangedCallbacks_.callAllCallbacks(AiCapability::Nlp, deployType);
    modelChangedCallbacks_.callAllCallbacks(AiCapability::Speech, deployType);
    modelChangedCallbacks_.callAllCallbacks(AiCapability::Vision, deployType);

    switch (deployType) {
        case DeployType::Local: {
            ondeviceSettings_.setIntValue("priority", priority);
            return true;
        }
        case DeployType::Custom: {
            customSettings_.setIntValue("priority", priority);
            return true;
        }
        case DeployType::PublicCloud: {
            cloudSettings_.setIntValue("priority", priority);
            return true;
        }
    }
    return false;
}

int ModelConfigManager::getModelSchedulingPriority(DeployType deployType) const {
    switch (deployType) {
        case DeployType::Local: {
            return ondeviceSettings_.getIntValue("priority");
        }
        case DeployType::Custom: {
            return customSettings_.getIntValue("priority");
        }
        case DeployType::PublicCloud: {
            return cloudSettings_.getIntValue("priority");
        }
    }
    return -1;
}

bool ModelConfigManager::setModelDeployEnabled(DeployType deployType, bool enabled) {
    modelChangedCallbacks_.callAllCallbacks(AiCapability::Nlp, deployType);
    modelChangedCallbacks_.callAllCallbacks(AiCapability::Speech, deployType);
    modelChangedCallbacks_.callAllCallbacks(AiCapability::Vision, deployType);

    switch (deployType) {
        case DeployType::Local:
            return ondeviceSettings_.setBoolValue("enabled", enabled);
        case DeployType::Custom:
            return customSettings_.setBoolValue("enabled", enabled);
        case DeployType::PublicCloud:
            return cloudSettings_.setBoolValue("enabled", enabled);
    }
    return false;
}

bool ModelConfigManager::needToMigrateOldModelConfig(const std::string &modelName) {
    if (util::readFile(oldModelConfigFile).empty()) {
        return false;
    }

    const auto autehns = getModelAuthentications(modelName);
    return autehns.empty() || autehns.back().authentications.back().value.empty();
}

// 旧版本的配置只有百度的，并且只有nlp的vision的能力
void ModelConfigManager::migrateOldModelConfig(const std::string &modelName, AiCapability capability) {
    std::vector<ModelInfo> odlModelInfos;
    const std::string oldVisionModelName = "百度-文心AI作画高级版";
    const std::string modelInfo = util::readFile(oldModelConfigFile);
    const Json::Value root = util::stringToJson(modelInfo);
    // 百度
    if (!root.isMember("0")) {
        return;
    }
    Json::Value baiduModelInfo = root["0"];
    const std::string capabilityKey = capability == AiCapability::Nlp ? "0" : "1";
    if (!baiduModelInfo.isMember(capabilityKey)) {
        return;
    }

    Json::Value nlpModelInfo = baiduModelInfo[capabilityKey];
    const std::string apiKey = nlpModelInfo["apiKey"].asString();
    const std::string secretKey = nlpModelInfo["secretKey"].asString();
    const std::string currentModelName = getCurrentModelName(capability, DeployType::PublicCloud);
    setModelAuthentications(modelName, {{"",
                                         {{"APIKey", "apiKey", Encryption::getInstance().decrypt(apiKey)},
                                          {"SecretKey", "secretKey", Encryption::getInstance().decrypt(secretKey)}}}});
    if (currentModelName.empty()) {
        setCurrentModelName(capability, DeployType::PublicCloud, modelName);
    }
}

void ModelConfigManager::loadModelInfos() {
    auto allEngineNames = aiEnginePluginManager_.allEngineName();
    for (const auto &engineName : allEngineNames) {
        loadNlpModelInfos(engineName);
        loadSpeechModelInfos(engineName);
        loadVisionModelInfos(engineName);
    }

    for (auto &model : modelInfos_) {
        LOGI("Model {} loaded", model.model.modelName);
    }
    for (auto &model : freeTrialModelInfos_) {
        LOGI("Trial Model {} loaded", model.model.modelName);
    }
}

void ModelConfigManager::loadNlpModelInfos(const std::string &engineName) {
    auto start = std::chrono::high_resolution_clock::now();
    auto engine = aiEnginePluginManager_.createTextGenerationEngine(engineName);
    if (!engine) {
        return;
    }
    auto end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double, std::milli> duration = end - start;
    LOGI("Time taken to create engine NLP {} model infos: {} ms", engineName, duration.count());

    std::vector<ModelInfo> models;
    if (engine->isCloud() && !engine->isCustomModelEngine()) {
        auto cloudEngine = static_cast<ai_engine::gen_ai::nlp::AbstractTextGenerationEngine *>(engine.get());
        models = createModelInfosFromJsonString(cloudEngine->modelInfo(), AiCapability::Nlp, DeployType::PublicCloud,
                                                engineName);
        if (engineName == "baichuan" || engineName == "sensetime") {
            if (!isCloudModelConfigured(models)) {
                LOGD("Model {} not configured", engineName);
                return;
            }
        }

        if (engineName == "freetrial") {
            freeTrialModelInfos_.insert(freeTrialModelInfos_.end(), models.begin(), models.end());
            return;
        }
    } else if (engine->isCustomModelEngine()) {
        models = createCustomModelInfosFromJsonString(customSettings_.getJsonValue("nlp"), engineName);
    } else if (!engine->isCloud()) {
        auto start = std::chrono::high_resolution_clock::now();
        models = createModelInfosFromJsonString(engine->modelInfo(), AiCapability::Nlp, DeployType::Local, engineName);
        auto end = std::chrono::high_resolution_clock::now();
        std::chrono::duration<double, std::milli> duration = end - start;
        LOGI("Time taken to get modelInfo NLP {} model infos: {} ms", engineName, duration.count());
        std::string jsonString = ondeviceSettings_.getJsonValue(aiCapabilityString(AiCapability::Nlp));
        Json::Value config = util::stringToJson(jsonString);
        removeOndeviceModelConfigIfNotExist(models, config);

        if (!config.isMember("currentModel")) {
            config["currentModel"] = "";
        }
        // 如果为空，且已安装本地模型，则设置默认值
        if (config["currentModel"].asString().empty()) {
            if (!models.empty()) {
                config["currentModel"] = models[0].model.modelName;
            }
        }
        ondeviceSettings_.setJsonValue(aiCapabilityString(AiCapability::Nlp), util::jsonToString(config));
    }

    std::lock_guard<std::mutex> lock(modelInfosMutex_);
    modelInfos_.insert(modelInfos_.end(), models.begin(), models.end());
}

void ModelConfigManager::removeOndeviceModelConfigIfNotExist(const std::vector<ModelInfo> &modelInfos,
                                                             Json::Value &config) {
    if (!config.isMember("currentModel")) {
        return;
    }

    const std::string currentModelName = config["currentModel"].asString();
    bool isCurrentModelExists = false;
    for (const auto &modelInfo : modelInfos) {
        if (modelInfo.model.deployType == DeployType::Local) {
            if (currentModelName == modelInfo.model.modelName) {
                isCurrentModelExists = true;
                break;
            }
        }
    }

    if (!isCurrentModelExists) {
        config.removeMember("currentModel");
        LOGD("Remove ondevice current model config: {}", currentModelName);
    }
}

void ModelConfigManager::loadSpeechModelInfos(const std::string &engineName) {
    auto engine = aiEnginePluginManager_.createSpeechEngine(engineName);
    if (engine != nullptr) {
        DeployType deployType{DeployType::Unknown};
        if (engine->isCustomModelEngine()) {
            deployType = DeployType::Custom;
        } else if (engine->isCloud()) {
            deployType = DeployType::PublicCloud;
        } else {
            deployType = DeployType::Local;
        }
        auto models = createModelInfosFromJsonString(engine->modelInfo(), AiCapability::Speech, deployType, engineName);

        std::lock_guard<std::mutex> lock(modelInfosMutex_);
        modelInfos_.insert(modelInfos_.end(), models.begin(), models.end());
    }
}

void ModelConfigManager::loadVisionModelInfos(const std::string &engineName) {
    auto engine = aiEnginePluginManager_.createImageGenerationEngine(engineName);
    if (!engine) {
        return;
    }

    if (engine->isCloud()) {
        auto cloudEngine = static_cast<ai_engine::gen_ai::vision::AbstractImageGenerationEngine *>(engine.get());
        auto models = createModelInfosFromJsonString(cloudEngine->modelInfo(), AiCapability::Vision,
                                                     DeployType::PublicCloud, engineName);

        std::lock_guard<std::mutex> lock(modelInfosMutex_);
        modelInfos_.insert(modelInfos_.end(), models.begin(), models.end());
    } else {
        auto models =
            createModelInfosFromJsonString(engine->modelInfo(), AiCapability::Vision, DeployType::Local, engineName);

        std::lock_guard<std::mutex> lock(modelInfosMutex_);
        modelInfos_.insert(modelInfos_.end(), models.begin(), models.end());
    }
}

bool ModelConfigManager::containsModel(const std::string &modelName) const {
    std::lock_guard<std::mutex> lock(modelInfosMutex_);
    bool isExistModel = std::find_if(modelInfos_.begin(), modelInfos_.end(), [&modelName](const ModelInfo &m) {
                            return m.model.modelName == modelName;
                        }) != modelInfos_.end();
    bool isExistFreeTrialModel =
        std::find_if(freeTrialModelInfos_.begin(), freeTrialModelInfos_.end(), [&modelName](const ModelInfo &m) {
            return m.model.modelName == modelName;
        }) != freeTrialModelInfos_.end();
    return isExistModel || isExistFreeTrialModel;
}

ModelInfo ModelConfigManager::getModelInfo(const std::string &modelName) const {
    waitForModelInfosLoaded();

    std::lock_guard<std::mutex> lock(modelInfosMutex_);
    auto it = std::find_if(modelInfos_.begin(), modelInfos_.end(),
                           [&modelName](const ModelInfo &m) { return m.model.modelName == modelName; });

    if (it != modelInfos_.end()) {
        return *it;
    }

    auto freeTrialIt = std::find_if(freeTrialModelInfos_.begin(), freeTrialModelInfos_.end(),
                                    [&modelName](const ModelInfo &m) { return m.model.modelName == modelName; });

    if (freeTrialIt != freeTrialModelInfos_.end()) {
        return *freeTrialIt;
    }

    return {};
}

bool ModelConfigManager::addCustomModel(const std::string &modelName, const std::string &modelVersion,
                                        const std::string &apiKey, const std::string &apiUrl) {
    waitForModelInfosLoaded();

    if (containsModel(modelName)) {
        LOGE("该模型已被添加 {}", modelName);
        return false;
    }
    Json::Value currentCustomJsonConfig = util::stringToJson(customSettings_.getJsonValue("nlp"));
    currentCustomJsonConfig[modelName]["name"] = modelName;
    currentCustomJsonConfig[modelName]["apiKey"] = Encryption::getInstance().encrypt(apiKey);
    currentCustomJsonConfig[modelName]["apiUrl"] = apiUrl;
    currentCustomJsonConfig[modelName]["model"] = modelVersion;
    LOGD("AddCustomModel: {}", currentCustomJsonConfig.toStyledString());
    // 当前自定义模型只支持nlp模型
    customSettings_.setJsonValue("nlp", currentCustomJsonConfig.toStyledString());

    std::lock_guard<std::mutex> lock(modelInfosMutex_);

    Model model{.modelName = modelName,
                .modelVersion = modelVersion,
                .apiUrl = apiUrl,
                .vendor = "",
                .debName = "",
                .is3rdModel = false,
                .capability = AiCapability::Nlp,
                .deployType = DeployType::Custom,
                .engineName = "custom"};

    MultiAuthentication multiAuthentication{
        .itemName = std::string{},
        .authentications = std::vector{Authentication{.friendlyKeyName = "APIKey", .key = "apiKey", .value = apiKey}}};

    modelInfos_.push_back(ModelInfo{.model = model, .multiAuthentications = std::vector{multiAuthentication}});
    return true;
}

bool ModelConfigManager::deleteCustomModel(const std::string &modelName) {
    Json::Value currentCustomJsonConfig = util::stringToJson(customSettings_.getJsonValue("nlp"));
    if (!currentCustomJsonConfig.isMember(modelName)) {
        return false;
    }

    waitForModelInfosLoaded();

    currentCustomJsonConfig.removeMember(modelName);
    if (currentCustomJsonConfig.isMember("currentModel") && currentCustomJsonConfig["currentModel"].isString() &&
        currentCustomJsonConfig["currentModel"].asString() == modelName) {
        currentCustomJsonConfig["currentModel"] = "";
    }

    customSettings_.setJsonValue("nlp", currentCustomJsonConfig.toStyledString());

    std::lock_guard<std::mutex> lock(modelInfosMutex_);
    auto it = std::remove_if(modelInfos_.begin(), modelInfos_.end(),
                             [&modelName](const ModelInfo &m) { return m.model.modelName == modelName; });

    modelInfos_.erase(it, modelInfos_.end());

    return true;
}

bool ModelConfigManager::setCustomModelApiUrl(const std::string &modelName, const std::string &apiUrl) {
    Json::Value currentCustomJsonConfig = util::stringToJson(customSettings_.getJsonValue("nlp"));
    if (!currentCustomJsonConfig.isMember(modelName)) {
        return false;
    }
    currentCustomJsonConfig[modelName]["apiUrl"] = apiUrl;
    LOGI("SetCustomModelApiUrl: {}", currentCustomJsonConfig.toStyledString());
    customSettings_.setJsonValue("nlp", currentCustomJsonConfig.toStyledString());

    waitForModelInfosLoaded();

    std::lock_guard<std::mutex> lock(modelInfosMutex_);
    for (auto &model : modelInfos_) {
        if (model.model.modelName == modelName) {
            model.model.apiUrl = apiUrl;
            break;
        }
    }
    return true;
}

bool ModelConfigManager::setCustomModelVersion(const std::string &modelName, const std::string &modelVersion) {
    Json::Value currentCustomJsonConfig = util::stringToJson(customSettings_.getJsonValue("nlp"));
    if (!currentCustomJsonConfig.isMember(modelName)) {
        return false;
    }

    currentCustomJsonConfig[modelName]["model"] = modelVersion;
    customSettings_.setJsonValue("nlp", currentCustomJsonConfig.toStyledString());

    waitForModelInfosLoaded();

    std::lock_guard<std::mutex> lock(modelInfosMutex_);
    for (auto &model : modelInfos_) {
        if (model.model.modelName == modelName) {
            model.model.modelVersion = modelVersion;
            break;
        }
    }
    return true;
}

void ModelConfigManager::updateCallback(AiCapability capability, DeployType deployType, const std::string &uuid,
                                        const ModelChangedCallbacks::ModelChangedCallback &callback) {
    return modelChangedCallbacks_.updateCallback(capability, deployType, uuid, callback);
}

void ModelConfigManager::updateAllDeployTypeCallback(AiCapability capability, const std::string &uuid,
                                                     const ModelChangedCallbacks::ModelChangedCallback &callback) {
    updateCallback(capability, DeployType::Local, uuid, callback);
    updateCallback(capability, DeployType::PublicCloud, uuid, callback);
    updateCallback(capability, DeployType::Custom, uuid, callback);
}

void ModelConfigManager::getConfiguredCloudNlpModelList(Json::Value &value) const {
    bool publicCloudEnabled = getModelDeployEnabled(DeployType::PublicCloud);
    if (!publicCloudEnabled) {
        return;
    }

    // 先获取免费模型
    std::vector<Model> freeTrialModels = getFreeTrialModels();
    for (const Model &model : freeTrialModels) {
        Json::Value subValue;
        if (model.deployType != DeployType::PublicCloud) {
            continue;
        }
        subValue["modelName"] = model.modelName;
        subValue["deployType"] = int(model.deployType);
        value.append(subValue);
    }
    // 非免费模型
    std::vector<Model> models = getModels(AiCapability::Nlp);
    for (const Model &model : models) {
        Json::Value subValue;
        ModelInfo modelInfo = getModelInfo(model.modelName);
        std::vector<MultiAuthentication> multiAuthentications = getModelAuthentications(model.modelName);
        Json::Value jsonMultiAuthentications = authensToEngineConfigJson(multiAuthentications);
        if (model.deployType != DeployType::PublicCloud) {
            continue;
        }
        if (!jsonMultiAuthentications.isMember("apiKey")) {
            continue;
        }
        if (!jsonMultiAuthentications["apiKey"].isString()) {
            continue;
        }
        if (jsonMultiAuthentications["apiKey"].asString().empty()) {
            continue;
        }
        subValue["modelName"] = model.modelName;
        subValue["deployType"] = int(model.deployType);
        value.append(subValue);
    }
}

void ModelConfigManager::getConfiguredLocalNlpModelList(Json::Value &value) const {
    bool localEnabled = getModelDeployEnabled(DeployType::Local);
    if (!localEnabled) {
        return;
    }

    std::vector<Model> models = getModels(AiCapability::Nlp);
    for (const Model &model : models) {
        Json::Value subValue;
        if (model.deployType != DeployType::Local) {
            continue;
        }
        subValue["modelName"] = model.modelName;
        subValue["deployType"] = int(model.deployType);
        value.append(subValue);
    }
}

void ModelConfigManager::getConfiguredCustomNlpModelList(Json::Value &value) const {
    bool customEnabled = getModelDeployEnabled(DeployType::Custom);
    if (!customEnabled) {
        return;
    }
    std::vector<Model> models = getModels(AiCapability::Nlp);
    for (const Model &model : models) {
        Json::Value subValue;
        if (model.deployType != DeployType::Custom) {
            continue;
        }
        subValue["modelName"] = model.modelName;
        subValue["deployType"] = int(model.deployType);
        value.append(subValue);
    }
}

void ModelConfigManager::waitForModelInfosLoaded() const {
    if (isModelInfosLoaderThread) {
        return;
    }
    if (modelInfosLoadFuture_.valid()) {
        if (modelInfosLoadFuture_.wait_for(std::chrono::seconds(25)) == std::future_status::ready) {
            try {
                modelInfosLoadFuture_.get();
            } catch (const std::exception &e) {
                LOGE("Exception during model info loading: {}", e.what());
            }
        } else {
            LOGW("Model infos loading is taking too long, please check the configuration.");
        }
    }
}

bool ModelConfigManager::isLocalModelEnabled() const { return getModelDeployEnabled(DeployType::Local); }

std::string ModelConfigManager::getConfiguredNlpModelList() const {
    Json::Value value = Json::arrayValue;
    // 需求写死的顺序：云端模型、本地模型、自定义模型
    getConfiguredCloudNlpModelList(value);
    getConfiguredLocalNlpModelList(value);
    getConfiguredCustomNlpModelList(value);
    return util::jsonToString(value);
}

std::vector<Model> ModelConfigManager::getFreeTrialModels() const {
    waitForModelInfosLoaded();

    std::vector<Model> models;

    for (const auto &modelInfo : freeTrialModelInfos_) {
        models.push_back(modelInfo.model);
    }

    return models;
}

bool ModelConfigManager::isCloudModelConfigured(const std::vector<ModelInfo> &modelInfos) {
    for (const auto &modelInfo : modelInfos) {
        std::string modelName = modelInfo.model.modelName;

        const std::string capabilityKey = aiCapabilityString(modelInfo.model.capability);

        auto &settings = modelInfo.model.deployType == DeployType::Custom ? customSettings_ : cloudSettings_;

        const std::string capabilityJsonString = settings.getJsonValue(capabilityKey);
        Json::Value jsonConfig = util::stringToJson(capabilityJsonString);
        if (!jsonConfig.isMember(modelName)) {
            return false;
        }
        Json::Value jsonAuths = jsonConfig[modelName];

        for (const auto &multiAuthentication : jsonToAuthenticationsWithDecryption(jsonAuths)) {
            for (const auto &authentication : multiAuthentication.authentications) {
                if (authentication.value.empty()) {
                    return false;
                }
            }
        }
    }
    return true;
}

bool ModelConfigManager::isModelEnabled(const std::string &modelName) const {
    // 使能关了的模型认为不存在
    bool publicCloudEnabled = getModelDeployEnabled(DeployType::PublicCloud);
    bool customEnabled = getModelDeployEnabled(DeployType::Custom);
    bool localEnabled = getModelDeployEnabled(DeployType::Local);

    ModelInfo modelInfo = getModelInfo(modelName);
    if (modelInfo.model.modelName.empty()) {
        return false;
    }
    if (!publicCloudEnabled && modelInfo.model.deployType == DeployType::PublicCloud) {
        return false;
    }
    if (!customEnabled && modelInfo.model.deployType == DeployType::Custom) {
        return false;
    }
    if (!localEnabled && modelInfo.model.deployType == DeployType::Local) {
        return false;
    }
    return true;
}

bool ModelConfigManager::isLocalModel(const std::string &modelName) const {
    if (modelName.empty()) {
        return false;
    }
    ModelInfo modelInfo = getModelInfo(modelName);
    return modelInfo.model.deployType == DeployType::Local;
}

bool ModelConfigManager::isNlpModelConfigured(const std::string &modelName) const {
    if (modelName.empty()) {
        return false;
    }
    ModelInfo modelInfo = getModelInfo(modelName);
    if (modelInfo.model.capability != AiCapability::Nlp) {
        return true;
    }
    if (modelInfo.model.deployType != DeployType::PublicCloud) {
        return true;
    }
    if (modelInfo.model.engineName == "freetrial") {
        return true;
    }
    std::vector<MultiAuthentication> multiAuthentications = getModelAuthentications(modelName);
    Json::Value jsonMultiAuthentications = authensToEngineConfigJson(multiAuthentications);
    if (!jsonMultiAuthentications.isMember("apiKey")) {
        return false;
    }
    if (!jsonMultiAuthentications["apiKey"].isString()) {
        return false;
    }
    if (jsonMultiAuthentications["apiKey"].asString().empty()) {
        return false;
    }
    return true;
}
