/*
 * Copyright (c) KylinSoft  Co., Ltd. 2024. All rights reserved.
 *
 * kaiming is licensed under the GPL v2.0+.
 * 
 * See the LICENSE file for more details.
 */

#include "KMUpdate.h"

#include <fcntl.h>
#include <sys/personality.h>
#include <grp.h>
#include <pwd.h>

#include "common/KMPackageManager.h"
#include "common/KMError.h"
#include "common/KMLogger.h"
#include "common/KMUtil.h"
#include "common/KMStringUtils.h"

/**
 * @brief 更新子命令的参数解析
 */
class KMUpdate::Options : public KMOption::Options
{
public:
    Options() = default;
    ~Options() override = default;

protected:
    /**
     * @brief 命令行参数解析后操作
     */
    void preParseHook() override;

    /**
     * @brief 命令行参数解析后操作
     */
    void postParseHook() override;

private:
    /**
     * @brief 命令行参数添加描述选项
     */
    void addOptions();

public:
    bool m_help;
    bool m_assumeYes;
    string m_arch;
    vector<string> m_vecRefs;
};

void KMUpdate::Options::preParseHook()
{
    addOptions();
}

/**
 * @brief 命令行参数解析后操作
 */
void KMUpdate::Options::postParseHook()
{
    if (m_help)
    {
        showUsage();
        exit(EXIT_SUCCESS);
    }
}

/**
 * @brief 命令行参数添加描述选项
 */
void KMUpdate::Options::addOptions()
{
    setDescription(_("\nUsage:\n \tkaiming update [options] [quote…] - upgrade package description\n"));
    //帮助选项
    addOption("help","h", KMOption::value<bool>(&m_help)->defaultValue(false), _("display Help Options"));
    addOption("assumeyes", "y", KMOption::value<bool>(&m_assumeYes)->defaultValue(false), _("automatic answer to all questions is yes"));

    //应用选项
    addOption("arch","",KMOption::value<std::string>(&m_arch),_("specify architecture"));
    //位置选项
    addPositionOption("REFS", KMOption::value<std::vector<std::string>>(&m_vecRefs),-1, _("specify the application REF"));
}

class KMUpdate::Private
{
public:
    std::shared_ptr<Options> m_options;
    std::shared_ptr<KMFolder> m_folder;
    std::shared_ptr<KMUtil> m_util;
    std::shared_ptr<KMEnv> m_env;
    std::shared_ptr<KMOSTreeHandler> m_ostreeHandler;
    std::shared_ptr<KMPackageManager> m_packageManager;

    bool m_isYes = false;
    std::string m_arch;
    std::string m_remoteRepoName;
    std::string m_repoIndexUrl;
    std::string m_repoIndexLocalGzPath;
    std::string m_repoIndexLocalSubPath;

    KMCommonRef m_appRef;                  // 单独ref,用于存储到 d->m_refUserList中
    vector<KMCommonRef> m_refUserList;     // 用户输入的待安装软件列表
    vector<KMCommonRef> m_refDownloadList; // 实际上需要安装的软件列表

    map<string, string> m_localRefs; // 本地安装的软件包列表
};

REGISTER_SUBCOMMAND_DYNCREATE(update, KMUpdate)

static vector<KMCommonRef> g_refUpdateListForProgress;               // 实际上需要安装的软件列表,用于进度展示
static UpdateCallBack g_updateProgressCallback;                      // 回调函数
static UpdateProgressStatus updateProgressStatus = { "", 0, "", 5 }; // 安装进度回调函数参数
static string updateProgressJson = "";

static string lastOutput; // 保存最后一次进度条输出
static string globalLastId;
static string preRemaining = ""; // 保存先前的剩余时间

KMUpdate::KMUpdate()
    : d(std::make_unique<Private>())
{
    init();
}

KMUpdate::~KMUpdate() = default;

int KMUpdate::dispose(int argc, char **argv)
{
    vector<string> vecUpdate;
    for (int i = 1; i < argc; i++)
    {
        if (argv[i][0] != '-')
        {
            vecUpdate.emplace_back(argv[i]);
        }
        else if (!vecUpdate.empty())
        {
            kmlogger.error("args error");
            return KM_ERROR_INVALID_ARGS;
        }
    }

    init();

    // 参数解析
    d->m_options->parseCommandLine(argc, argv);

    int ret = start(vecUpdate);

    return ret;
}

/**
 * @brief 开始对已获取的用户软件包列表执行安装
 *
 * @param vecName
 * @return int
 */
int KMUpdate::start(const vector<string> &vecName)
{
    PackageManagerOptions pmOptions;
    pmOptions.assumeYes = d->m_options->m_assumeYes;
    pmOptions.arch = d->m_options->m_arch;

    int ret = d->m_packageManager->processTask(TASK_TYPE_UPDATE, vecName, pmOptions);

    return ret;
}

void KMUpdate::init()
{
    d->m_options = std::make_shared<Options>();
    d->m_folder = std::make_shared<KMFolder>("/opt/kaiming", false);
    d->m_util = std::make_shared<KMUtil>();
    d->m_env = std::make_shared<KMEnv>();
    fs::path repoPath = d->m_folder->getRepoPath();
    d->m_ostreeHandler = std::make_shared<KMOSTreeHandler>(d->m_folder->getRepoPath());
    d->m_packageManager = std::make_shared<KMPackageManager>();
    d->m_localRefs = d->m_ostreeHandler->getLocalRefsDeployedMap(REPO_NAME);
}
