/*
 * 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/>.
 */

#pragma once

#include <jsoncpp/json/json.h>

#include <future>

#include "engine/aienginepluginmanager.h"
#include "modelchangedcallbacks.h"
#include "modelconfigenums.h"
#include "modelsettings.h"

struct Authentication {
    std::string friendlyKeyName;
    std::string key;
    std::string value;
};

struct MultiAuthentication {
    std::string itemName;
    std::vector<Authentication> authentications;
};

struct Model {
    std::string modelName;
    std::string modelVersion;
    std::string apiUrl;
    std::string vendor;
    std::string debName;
    bool is3rdModel{false};
    AiCapability capability;
    DeployType deployType;
    std::string engineName;
};

struct ModelInfo {
    Model model;
    std::vector<MultiAuthentication> multiAuthentications;
};

class ModelConfigManager {
public:
    static ModelConfigManager &getInstance();

    ModelConfigManager(const ModelConfigManager &) = delete;

    ModelConfigManager &operator=(const ModelConfigManager &) = delete;

    std::vector<Model> getModels(AiCapability capability) const;
    std::vector<Model> getModels(DeployType deployType) const;
    std::string getModelVersionByModelName(const std::string &modelName) const;
    std::string getCurrentModelName(AiCapability capability, DeployType deployType) const;
    std::vector<MultiAuthentication> getModelAuthentications(const std::string &modelName) const;
    bool getModelDeployEnabled(DeployType deployType) const;

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

    bool setModelAuthentications(const std::string &modelName, const std::vector<MultiAuthentication> &authentications);

    bool clearModelAuthentications(const std::string &modelName);

    bool setModelSchedulingPriority(DeployType deployType, int priority);

    int getModelSchedulingPriority(DeployType deployType) const;

    bool setModelDeployEnabled(DeployType deployType, bool enabled);

    ModelInfo getModelInfo(const std::string &modelName) const;

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

    bool deleteCustomModel(const std::string &modelName);

    bool setCustomModelApiUrl(const std::string &modelName, const std::string &apiUrl);
    bool setCustomModelVersion(const std::string &modelName, const std::string &modelVersion);

    void updateCallback(AiCapability capability, DeployType deployType, const std::string &uuid,
                        const ModelChangedCallbacks::ModelChangedCallback &callback);

    void updateAllDeployTypeCallback(AiCapability capability, const std::string &uuid,
                                     const ModelChangedCallbacks::ModelChangedCallback &callback);

    std::string getConfiguredNlpModelList() const;

    std::vector<Model> getFreeTrialModels() const;

    bool isCloudModelConfigured(const std::vector<ModelInfo> &modelInfos);

    bool isModelEnabled(const std::string &modelName) const;

    bool isLocalModel(const std::string &modelName) const;

    bool isNlpModelConfigured(const std::string &modelName) const;

    void waitForModelInfosLoaded() const;

    bool isLocalModelEnabled() const;

private:
    explicit ModelConfigManager(ai_engine::AiEnginePluginManager &aiEnginePluginManager);

    bool needToMigrateOldModelConfig(const std::string &modelName);
    void migrateOldModelConfig(const std::string &modelName, AiCapability capability);
    void loadModelInfos();
    void loadNlpModelInfos(const std::string &engineName);
    void loadSpeechModelInfos(const std::string &engineName);
    void loadVisionModelInfos(const std::string &engineName);

    void removeOndeviceModelConfigIfNotExist(const std::vector<ModelInfo> &modelInfos, Json::Value &config);

    bool containsModel(const std::string &modelName) const;

    void updateModelAuthenticationsSettings(Settings &settings, const ModelInfo &modelInfo,
                                            const std::vector<MultiAuthentication> &authentications);
    void updateModelAuthenticationsInfo(const std::string &modelName,
                                        const std::vector<MultiAuthentication> &authentications);

    void clearModelAuthenticationsSettings(Settings &settings, const ModelInfo &modelInfo);
    void clearModelAuthenticationsInfo(const std::string &modelName);
    void setDefaultCloudNlpModelIfNotCurrentSet();

    void getConfiguredCloudNlpModelList(Json::Value &value) const;

    void getConfiguredLocalNlpModelList(Json::Value &value) const;

    void getConfiguredCustomNlpModelList(Json::Value &value) const;

private:
    ai_engine::AiEnginePluginManager &aiEnginePluginManager_;
    Settings cloudSettings_;
    Settings ondeviceSettings_;
    Settings customSettings_;
    std::vector<ModelInfo> modelInfos_;
    std::vector<ModelInfo> freeTrialModelInfos_;
    ModelChangedCallbacks modelChangedCallbacks_;
    mutable std::mutex modelInfosMutex_;
    std::shared_future<void> modelInfosLoadFuture_;
};
