/*
 * 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 "KMInstall.h"

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

#include "common/KMError.h"
#include "common/KMLogger.h"
#include "common/KMUtil.h"
#include "syslog.h"

using namespace nlohmann;

/**
 * @brief 安装子命令的参数解析
 */
class KMInstall::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;
    bool m_skipSignatureVerification;
    bool m_force;
    string m_arch;
    string m_kind;
    vector<string> m_vecRefs;
};

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

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

/**
 * @brief 命令行参数添加描述选项
 */
void KMInstall::Options::addOptions()
{
    setDescription(_("\nUsage:\n \tkaiming install [option…] [quote…] - Install applications or runtime\n"));
    //帮助选项
    addOption("help", "h", KMOption::value<bool>(&m_help)->defaultValue(false), _("display help information"));
    addOption("assumeyes", "y", KMOption::value<bool>(&m_assumeYes)->defaultValue(false), _("automatic answer to all questions is yes"));
    addOption("force", "i", KMOption::value<bool>(&m_force)->defaultValue(false), _("force installation of already installed applications"));
    // TODO:先默认跳过签名
    addOption("skip-signature-verification", "", KMOption::value<bool>(&m_skipSignatureVerification)->defaultValue(true), _("skip signature verification during installation"));

    //应用选项
    addOption("arch", "", KMOption::value<std::string>(&m_arch), _("specify architecture"));
    addOption("kind", "", KMOption::value<std::string>(&m_kind), _("specify the type, such as app, runtime, base, extension, depend"));

    //位置选项
    addPositionOption("REFS", KMOption::value<std::vector<std::string>>(&m_vecRefs), -1, _("specify the application REF, the supported formats are as follows:\n"
                                                                                           "  id\n"
                                                                                           "  id/version\n"
                                                                                           "  id/version/module\n"
                                                                                           "  id/version/module/channel"));
}

class KMInstall::Private
{
public:
    std::shared_ptr<Options> m_options;
    std::shared_ptr<KMFolder> m_folder;
    std::shared_ptr<KMPackageManager> m_packageManager;

    bool m_isYes = false;
    bool m_isOfflinePackage = false; // 区分离线包和在线包。 true: 离线包 false: 在线包
};

REGISTER_SUBCOMMAND_DYNCREATE(install, KMInstall)

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

KMInstall::~KMInstall() = default;

/**
 * @brief 命令行格式支持全部在线安装和全部离线安装两种格式，不支持混合在线和离线安装
 * 
 * 全部在线安装：id，或者全格式: kaiming-repo:stable/${id}/${version}/${arch}/${module} 
 * 全部离线安装：统一 .ok 后缀
 */
int KMInstall::dispose(int argc, char **argv)
{
    vector<string> vecInstall;

    init();

    d->m_options->parseCommandLine(argc, argv);

    if (d->m_options->m_vecRefs.size() < 1)
    {
        kmlogger.error("Args error");
        return KM_ERROR_INVALID_ARGS;
    }

    for (const auto &ref : d->m_options->m_vecRefs)
    {
        vecInstall.emplace_back(ref);
    }

    int ret = start(vecInstall);

    return ret;
}

/**
 * @brief 开始对已获取的用户软件包列表执行安装
 * 
 * 此处分为离线包和在线包下载。
 * 
 * 统一离线包格式： .ok 后缀 
 *
 * @param vectorAppIds 待安装的用户软件包列表
 * @return int
 */
int KMInstall::start(const vector<string> &vectorAppIds)
{
    PackageManagerOptions pmOptions;
    pmOptions.assumeYes = d->m_options->m_assumeYes;
    pmOptions.skipSignatureVerification = d->m_options->m_skipSignatureVerification;
    pmOptions.arch = d->m_options->m_arch;
    pmOptions.kind = d->m_options->m_kind;
    pmOptions.force = d->m_options->m_force;
    int ret = d->m_packageManager->processTask(TASK_TYPE_INSTALL, vectorAppIds, pmOptions);

    return ret;
}

void KMInstall::init()
{
    d->m_options = make_shared<Options>();
    d->m_folder = make_shared<KMFolder>("/opt/kaiming", false);
    d->m_packageManager = make_shared<KMPackageManager>();
}
