#include "KMUpdate.h"

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

#ifdef USE_SECCOMP
#  include <seccomp.h>
#endif

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

/**
 * @brief 更新子命令的参数解析
 */
class KMUpdate::Options : public KMOptions
{
public:
    Options() = default;
    virtual ~Options() = 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()
{
    m_optionsDescription = std::make_unique<opt::options_description>(_("\nUsage:\n \tkaiming update [OPTION…] [REF…] - Update applications or runtimes "));

    opt::options_description helpOptions(_("Help Options"));
    helpOptions.add_options()("help,h", opt::bool_switch(&m_help)->default_value(false), _("Show help options"));

    opt::options_description appOptions(_("Application Options"));
    appOptions.add_options()("assumeyes,y", opt::bool_switch(&m_assumeYes)->default_value(false), _("Automatically answer all questions yes"))
                            ("arch=ARCH", opt::value<string>(&m_arch), _("Architecture"));

    opt::options_description positionalOptions(_("Positional Options"));
    positionalOptions.add_options()("REF...", opt::value<vector<string>>(&m_vecRefs), _("REF..."));
    m_optionsDescription->add(helpOptions).add(appOptions).add(positionalOptions);

    m_optionsPositionalDescription->add("REF...", -1);
}

class KMUpdate::Private
{
public:
    std::shared_ptr<Options> m_options;
    std::shared_ptr<KMFolder> m_folder;
    std::shared_ptr<KMRemoteSummary> m_remoteSummary;
    std::shared_ptr<KMUtil> m_util;
    std::shared_ptr<KMEnv> m_env;
    std::shared_ptr<KMParseDeploy> m_parseDeploy;
    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 = ""; // 保存先前的剩余时间

/**
 * @brief 通过解析命令行参数获取 ref 信息
 *
 * 目前支持2种单个 ref 格式：
 *          org.kde.kclock
 *          app/org.kde.kclock/x86_64/master
 *
 * @param pref 命令行输入的appid参数，如 org.kde.kclock, app/org.kde.kclock/x86_64/master
 * @return KMCommonRef
 */
static KMCommonRef makeRefDataFromInitRefString(std::string pref)
{
    KMCommonRef ref;

    // 使用字符串流进行按"/"分割
    std::istringstream iss(pref);
    std::string token;
    std::vector<std::string> tokens;

    kmlogger.info("Input pref:  %s", pref.c_str());

    bool onlyAppid = false;

    if (KMUtil::startsWithForRef(pref, "app/"))
    {
        kmlogger.info("Input pref type app");
        ref.baseType = "app";
    }
    else if (KMUtil::startsWithForRef(pref, "runtime/"))
    {
        kmlogger.info("Input pref type runtime");
        ref.baseType = "runtime";
    }
    else
    {
        kmlogger.info("Input pref get default type");
        ref.baseType = KMUtil::getDefaultTypeForType(pref);
        onlyAppid = true;
    }

    kmlogger.debug("ref.baseType:  %s", ref.baseType.c_str());

    if (onlyAppid)
    {
        ref.id = pref;
        globalLastId = ref.id;
        ref.arch = KMUtil::kmGetArch();
        ref.branch = KMUtil::kmGetDefaultChannel(); // Todo: 默认返回 master, 但测试默认是 stable
        ref.completedId = ref.baseType.append("/").append(ref.id).append("/").append(ref.arch).append("/").append(ref.branch);

        kmlogger.info("complete ref string: %s", ref.completedId.c_str());
        return ref;
    }

    while (std::getline(iss, token, '/'))
    {
        kmlogger.debug("token:  %s", token.c_str());
        tokens.push_back(token);
    }

    ref.id = tokens[1];
    globalLastId = ref.id;
    ref.arch = tokens[2];
    ref.branch = tokens[3];
    ref.completedId = ref.baseType.append("/").append(ref.id).append("/").append(ref.arch).append("/").append(ref.branch);

    kmlogger.info("complete ref string::  %s", ref.completedId.c_str());

    return ref;
}

/**
 * @brief 根据完整的ref获取id
 *
 * @param pref
 * @return std::string
 */
static std::string getIdFromRef(string pref)
{
    // 使用字符串流进行按"/"分割
    std::istringstream iss(pref);
    std::string token;
    std::vector<std::string> tokens;

    kmlogger.info("Input pref:  %s", pref.c_str());

    bool onlyAppid = false;

    if (KMUtil::startsWithForRef(pref, "app/") || KMUtil::startsWithForRef(pref, "runtime/"))
    {
    }
    else
    {
        onlyAppid = true;
    }

    if (onlyAppid)
    {
        return pref;
    }

    while (std::getline(iss, token, '/'))
    {
        kmlogger.info("token:  %s", token.c_str());
        tokens.push_back(token);
    }

    string id = tokens[1];
    return id;
}

static int getWriteProgress(int outstandingWrites)
{
    return outstandingWrites > 0 ? (int)(3 / (double)outstandingWrites) : 3;
}

static char *formatDuration(guint64 duration)
{
    int h, m, s;

    m = duration / 60;
    s = duration % 60;
    h = m / 60;
    m = m % 60;

    if (h > 0)
    {
        return g_strdup_printf("%02d:%02d:%02d", h, m, s);
    }
    else
    {
        return g_strdup_printf("%02d:%02d", m, s);
    }
}

/**
 * @brief 更新进度信息
 *
 */
static void updateUpdateProgress()
// void KMUpdate::updateUpdateProgress()
{
    // 只有当 g_updateProgressCallback 是有效的时候才调用,即已经与某个可调用对象关联，并且没有被释放，避免 bad_function_call
    if (g_updateProgressCallback && updateProgressJson != "")
    {
        g_updateProgressCallback(updateProgressJson);
    }
}

static map<string, guint64> getAllRefsMap(string completedRef, guint64 &singleSize, guint64 &totalSize)
{
    map<string, guint64> refsMap;

    for (auto &ref : g_refUpdateListForProgress)
    {
        kmlogger.info("completedId: %s", ref.completedId.c_str());
        guint64 installedSize;
        if (ref.baseType == "app")
        {
            installedSize = ref.installedSizeInt;
        }
        else
        {
            installedSize = ref.downloadSize;
        }
        ref.realInstalledSize = installedSize;

        if (completedRef == ref.completedId)
        {
            singleSize = ref.realInstalledSize;
        }

        totalSize += installedSize;
        refsMap[ref.completedId] = installedSize;
    }

    return refsMap;
}

static map<string, guint64> getAllRefsMap(guint64 &totalSize)
{
    map<string, guint64> refsMap;

    for (auto &ref : g_refUpdateListForProgress)
    {
        guint64 installedSize;
        if (ref.baseType == "app")
        {
            installedSize = ref.installedSizeInt;
        }
        else
        {
            installedSize = ref.downloadSize;
        }

        totalSize += installedSize;
        refsMap[ref.completedId] = installedSize;
    }

    return refsMap;
}

static bool isZero(double value, double tolerance = 1e-6)
{
    return std::abs(value) < tolerance;
}

static std::string calculateDownloadTime(const std::string &speed, guint64 sizeBytes)
{
    long int downloadSpeed;
    std::string result;

    kmlogger.info("speed: %s sizeBytes: %ld", speed.c_str(), sizeBytes);

    // Parse the speed string to get the download speed in bytes/s
    if (speed.find("MB") != std::string::npos)
    {
        downloadSpeed = std::stol(speed.substr(0, speed.find("MB") - 1)) * 1024 * 1024; // Convert MB/s to bytes/s
    }
    else if (speed.find("kB") != std::string::npos)
    {
        downloadSpeed = std::stol(speed.substr(0, speed.find("kB") - 1)) * 1024; // Convert kB/s to bytes/s
    }
    else if (speed.find("bytes") != std::string::npos)
    {
        downloadSpeed = std::stol(speed.substr(0, speed.find("bytes") - 1));
        if (downloadSpeed == 0)
        {
            downloadSpeed = 5 * 1024; // 当 speed 属于 0 bytes/s 时，代表不下载，但进度也应该有，因此默认为5*1024 = 5KB 计算时间
            return "00:15";           // 单个下载进展到97时，也会出现 "0 bytes/s",因此设为默认 15 秒
        }
    }
    else
    {
        if (sizeBytes < 169887)
        {
            return "00:10"; // 169887为随机数，给出默认时间 10 秒
        }
        else
        {
            return "01:15"; // 给出默认时间 1 分钟
        }
    }
    kmlogger.info("speed: %s sizeBytes: %ld downloadSpeed=%ld", speed.c_str(), sizeBytes, downloadSpeed);

    // if (isZero(downloadSpeed))
    if (downloadSpeed == 0 && sizeBytes > 0)
    {
        // return "";
        downloadSpeed = 5 * 1024; // 当 speed 属于 0 bytes/s 时，代表不下载，但进度也应该有，因此默认为50计算时间
    }

    // Calculate the remaining download time in seconds
    int downloadTime = (int)sizeBytes / downloadSpeed;
    kmlogger.info("speed: %s sizeBytes: %ld downloadSpeed=%ld downloadTime=%d", speed.c_str(), sizeBytes, downloadSpeed, downloadTime);

    // Format the download time as hours:minutes:seconds or minutes:seconds
    if (downloadTime >= 3600)
    {
        int hours = static_cast<int>(downloadTime / 3600);
        int minutes = static_cast<int>((downloadTime % 3600) / 60);
        int seconds = static_cast<int>(downloadTime % 60);

        string hoursStr;
        if (hours >= 0 && hours <= 9)
        {
            hoursStr = string("0") + std::to_string(hours);
        }
        else if (hours > 9)
        {
            hoursStr = std::to_string(hours);
        }
        else
        {
            hoursStr = string("00");
        }

        string minutesStr;
        if (minutes >= 0 && minutes <= 9)
        {
            minutesStr = string("0") + std::to_string(minutes);
        }
        else if (minutes > 9)
        {
            minutesStr = std::to_string(minutes);
        }
        else
        {
            minutesStr = string("03");
        }

        string secondsStr;
        if (seconds >= 0 && seconds <= 9)
        {
            secondsStr = string("0") + std::to_string(seconds);
        }
        else if (seconds > 9)
        {
            secondsStr = std::to_string(seconds);
        }
        else
        {
            secondsStr = string("30");
        }

        result = hoursStr + ":" + minutesStr + ":" + secondsStr;
        kmlogger.info("speed: %s sizeBytes: %ld downloadSpeed=%ld downloadTime=%d result:%s", speed.c_str(), sizeBytes, downloadSpeed, downloadTime, result.c_str());
    }
    else
    {
        int minutes = static_cast<int>(downloadTime / 60);
        int seconds = static_cast<int>(downloadTime % 60);

        string minutesStr;
        if (minutes >= 0 && minutes <= 9)
        {
            minutesStr = string("0") + std::to_string(minutes);
        }
        else if (minutes > 9)
        {
            minutesStr = std::to_string(minutes);
        }
        else
        {
            minutesStr = string("03");
        }

        string secondsStr;
        if (seconds >= 0 && seconds <= 9)
        {
            secondsStr = string("0") + std::to_string(seconds);
        }
        else if (seconds > 9)
        {
            secondsStr = std::to_string(seconds);
        }
        else
        {
            secondsStr = string("30");
        }

        result = minutesStr + ":" + secondsStr;
        kmlogger.info("speed: %s sizeBytes: %ld downloadSpeed=%ld downloadTime=%d result:%s", speed.c_str(), sizeBytes, downloadSpeed, downloadTime, result.c_str());
    }

    if (result.find("-") != std::string::npos)
    {
        // 时间为负数不合理，此处先默认给出时间
        return "03:30";
    }

    return result;
}

/**
 * @brief 进度处理回调函数
 *
 * 进度条处理分成3个步骤：
 *  - 1. 当拉取 metadata 时， 增长到 5%;
 *  - 2. 下载数据一直到 97%;
 *  - 3. 写入 ostree objects 为最后的 3%
 *
 * @param progressStatus 在 ostree pull 时，发送的进度等信息结构体
 */
static void progress_cb(ProgressStatus progressStatus)
// void KMUpdate::progress_cb(ProgressStatus progressStatus)
{
    char buf[1024 * 8];
    guint64 total = 0;
    guint64 elapsedTime;
    guint newProgress = 0;
    gboolean estimating = FALSE;
    guint64 totalTransferred;
    gboolean lastWasMetadata = progressStatus.lastWasMetadata;
    gchar *formattedBytesTotalTransferred = NULL;

    int i;
    int fullSize, partial;
    char *speed = NULL;
    int barLength;
    const char *partialBlocks[] = {
        " ", "▏", "▎", "▍", "▌", "▋", "▊", "▉",
    };
    const char *fullBlock = "█";

    int rows = 24, cols = 80;

    elapsedTime = (g_get_monotonic_time() - progressStatus.startTime) / G_USEC_PER_SEC;

    if (*progressStatus.ostreeStatus)
    {
        // 当ostree_status 有反馈，则代表fetch成功，总进度到了100%
        kmlogger.info("update ostree status receive pull success");
        newProgress = 100;
        progressStatus.progress = 100;
    }
    else
    {
        totalTransferred = progressStatus.bytesTransferred + progressStatus.transferredExtraDataBytes;
        formattedBytesTotalTransferred = g_format_size_full(totalTransferred, G_FORMAT_SIZE_IEC_UNITS);

        progressStatus.lastWasMetadata = FALSE;

        if (progressStatus.totalDeltaParts == 0 && (progressStatus.outstandingMetadataFetches > 0 || lastWasMetadata) && progressStatus.metadataFetched < 20)
        {
            /* 在没有完成 metadata 处理时，我们需要执行2次回调函数，
                    因为有时会遇到仅处理元数据会触发调用，但却没进行处理和添加更多元数据。*/
            if (progressStatus.outstandingMetadataFetches > 0)
            {
                progressStatus.lastWasMetadata = TRUE;
            }

            /* At this point we don't really know how much data there is, so we have to make a guess.
             * Since its really hard to figure out early how much data there is we report 1% until
             * all objects are scanned. */

            estimating = TRUE;

            sprintf(buf, _("Downloading metadata: %u/(estimating) %s"), progressStatus.fetched, formattedBytesTotalTransferred);
            kmlogger.info("Downloading metadata buf: %s\n", buf);

            /* 增长到 5% 直到所有元数据都被拉取了 */
            newProgress = 0;
            if (progressStatus.requested > 0)
            {
                newProgress = progressStatus.fetched * 5 / progressStatus.requested;
            }
        }
        else
        {
            if (progressStatus.totalDeltaParts > 0)
            {
                gchar *formattedBytesTotal = NULL;

                /* 仅仅使用 deltas, 因此我们可以忽略 regular objects 来获得更好的 sizes;
                 *
                 * fetchedDeltaPartSize 所有 delta 部分 和 fallback objects 参数的总大小，
                 * 它是在最开始可以获得的，因此不用下载。
                 */
                total = progressStatus.totalDeltaPartSize - progressStatus.fetchedDeltaPartSize + progressStatus.totalExtraDataBytes;
                formattedBytesTotal = g_format_size_full(total, G_FORMAT_SIZE_IEC_UNITS);

                sprintf(buf, _("Downloading: %s/%s"), formattedBytesTotalTransferred, formattedBytesTotal);
                kmlogger.info("Downloading buf: %s\n", buf);
            }
            else
            {
                /* Non-deltas, 因此我们不能知道除了 object 计数和额外数据的字节大小以外的任何东西，
                       为了比较它们和额外数据，我们使用平均对象大小来评估总大小。*/
                double averageObjectSize = 1;
                if (progressStatus.fetched > 0)
                {
                    averageObjectSize = progressStatus.bytesTransferred / (double)progressStatus.fetched;
                }

                total = averageObjectSize * progressStatus.requested + progressStatus.totalExtraDataBytes;

                gchar *formattedBytesTotal = g_format_size_full(total, G_FORMAT_SIZE_IEC_UNITS);
                sprintf(buf, _("Downloading extra data: %s/%s"), formattedBytesTotalTransferred, formattedBytesTotal);
                kmlogger.info("下载额外数据 buf: %s\n", buf);

                sprintf(buf, _("Downloading files: %d/%d %s"), progressStatus.fetched, progressStatus.requested, formattedBytesTotalTransferred);
                kmlogger.info("下载文件: %s\n", buf);
            }

            /* 下载总进度到达 97% */
            if (total > 0)
            {
                newProgress = 5 + ((totalTransferred / (gdouble)total) * 92);
            }
            else
            {
                newProgress = 97;
            }

            /* 增加最后写入对象的 3% 进度 */
            newProgress += getWriteProgress(progressStatus.outstandingWrites);
        }

        if (elapsedTime > 0) // Ignore first second
        {
            gchar *formattedBytesSec = g_format_size(totalTransferred / elapsedTime);
            sprintf(buf, " (%s/s)", formattedBytesSec);
            kmlogger.info("5 buf: %s\n", buf);
        }

        if (newProgress < progressStatus.progress && progressStatus.lastTotal == total)
        {
            newProgress = progressStatus.progress;
        }

        progressStatus.lastTotal = total;
        kmlogger.info("newProgress: %d\n", newProgress);
        if (newProgress > 100)
        {
            if (!progressStatus.reportedOverflow)
            {
                kmlogger.error("Unexpectedly got > 100%% progress, limiting");
            }

            progressStatus.reportedOverflow = TRUE;
            newProgress = 100;
        }
    }

    guint percent = newProgress;

    guint singlePercent = newProgress; // 单一软件包下载进度
    guint totalPercent;                 // 所有包下载总进度

    guint64 totalSize = 0;
    guint64 singleSize = 0;
    map<string, guint64> installRefsMap = getAllRefsMap(progressStatus.ref, singleSize, totalSize);
    string currentRef = getIdFromRef(progressStatus.ref);

    guint currentProgress = 0; // 当前的进度
    string lastDownloadRef = g_refUpdateListForProgress.back().completedId;
    kmlogger.info("lastDownloadRef: %s", lastDownloadRef.c_str());

    for (const auto &item : g_refUpdateListForProgress) // 内部排序，因此不能顺序存储
    {
        kmlogger.info("completedId: %s size:%ld ", item.completedId.c_str(), item.realInstalledSize);
        if (item.completedId == progressStatus.ref)
        {
            currentProgress += (double)singlePercent / 100 * singleSize;
            break;
        }
        currentProgress += item.realInstalledSize;
    }

    totalPercent = (double)currentProgress * 100 / totalSize;

    kmlogger.info("singlePercent:%d singleSize: %ld currentProgress: %ld  totalSize: %ld totalPercent: %d", singlePercent, singleSize, currentProgress, totalSize, totalPercent);

    progressStatus.progress = totalPercent;
    progressStatus.estimating = estimating;
    int speedLen = 0;

    if (elapsedTime > 0)
    {
        char *formattedBytesSec = g_format_size(totalTransferred / elapsedTime);
        char *remaining = NULL;
        if (elapsedTime > 3 && singlePercent > 0)
        {
            guint64 totalTime = elapsedTime * 100 / (double)singlePercent;
            remaining = formatDuration(totalTime - elapsedTime);
            kmlogger.info("totalTime:%ld elapsedTime:%ld  remaining_time:%ld remaining: %s", totalTime, elapsedTime, totalTime - elapsedTime, remaining);
        }

        guint64 remainingBytes = totalSize - currentProgress;
        string remainingStr = calculateDownloadTime(string(formattedBytesSec), remainingBytes).c_str();
        kmlogger.info("remainingBytes: %ld remainingStr: %s preRemaining:%s", remainingBytes, remainingStr.c_str(), preRemaining.c_str());

        preRemaining = (remainingStr != "") ? remainingStr : preRemaining;
        // speed = g_strdup_printf("%s", formattedBytesSec);
        speed = g_strdup_printf("%s/s %s", formattedBytesSec, preRemaining.c_str());
        speedLen = MAX(speedLen, strlen(speed) + 2);
    }

    barLength = 40;
    fullSize = (barLength * totalPercent) / 100;
    partial = (((barLength * totalPercent) % 100) * G_N_ELEMENTS(partialBlocks)) / 100;

    char str[1024 * 8];
    sprintf(str, "%s %-20s ", "正在安装 ", currentRef.c_str());

    kmlogger.info("fullSize: %d barLength:%d", fullSize, barLength);
    for (i = 0; i < fullSize; i++)
    {
        sprintf(str + strlen(str), "%s", fullBlock);
    }

    if (i < barLength)
    {
        sprintf(str + strlen(str), "%-20s", partialBlocks[partial]);
        kmlogger.info("str: %s", str);
        i++;
    }

    sprintf(str + strlen(str), " %d%s", totalPercent, "%");

    if (*progressStatus.ostreeStatus)
    {
        // printf("\n");
        if (elapsedTime > 0)
        {
            char *formattedBytesSec = g_format_size(totalTransferred / elapsedTime);
            speed = g_strdup_printf("%s/s %s", formattedBytesSec, preRemaining.c_str());

            guint64 remainingBytes = totalSize - currentProgress;
            string remainingStr = calculateDownloadTime(string(formattedBytesSec), remainingBytes).c_str();
            kmlogger.info("remainingBytes: %ld remainingStr: %s preRemaining:%s", remainingBytes, remainingStr.c_str(), preRemaining.c_str());

            preRemaining = (remainingStr != "") ? remainingStr : preRemaining;
            speed = g_strdup_printf("%s/s %s", formattedBytesSec, preRemaining.c_str());
            speedLen = MAX(speedLen, strlen(speed) + 2);
        }
        else
        {
            // elapsedTime: 0
            kmlogger.info("elapsedTime == 0");
            speed = g_strdup_printf("%s/s %s", "5 MB", "03:30");
            speedLen = MAX(speedLen, strlen(speed) + 2);
        }
        if (speed)
        {
            sprintf(str + strlen(str), " %s", speed);
        }
    }
    else
    {
        if (speed)
        {
            sprintf(str + strlen(str), " %s", speed);
        }
    }

    // kmlogger.info("len(str): %d size(lastOutput): %d", strlen(str), lastOutput.size());
    if (strlen(str) <= lastOutput.size())
    {
        printf("\r"); // 移动光标到行首，但不显示尾部光标
        printf("%-150s", "");
    }
    printf("\r");

    printf("%s", str);
    lastOutput = string(str);

    kmlogger.info("currentRef: %s lastRef: %s progress", progressStatus.ref.c_str(), progressStatus.lastRef.c_str());
    if (progressStatus.ref == progressStatus.lastRef && *progressStatus.ostreeStatus)
    {
        printf("\n"); // 最后输出需要换行
    }

    if (speed)
    {
        kmlogger.info("speed: %s", speed);
    }
    else
    {
        speed = g_strdup_printf("0 bytes/s 00:00");
        speedLen = MAX(speedLen, strlen(speed) + 2);
    }

    updateProgressStatus.speed = string(speed); // todo: 存在段错误
    updateProgressStatus.refId = progressStatus.ref;
    updateProgressStatus.progress = totalPercent;
    updateProgressStatus.timeout = 5;

    kmlogger.info("update progress------>>:  refId:%s progress:%d speed:%s timeout:%d", updateProgressStatus.refId.c_str(), updateProgressStatus.progress, updateProgressStatus.speed.c_str(), updateProgressStatus.timeout);

    updateProgressJson = KMJsonHelper::packProgressInfoToJson(updateProgressStatus.refId, updateProgressStatus.progress, updateProgressStatus.speed, updateProgressStatus.timeout);
    updateUpdateProgress();

    fflush(stdout); // 刷新输出缓冲区
}

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

KMUpdate::~KMUpdate() { }

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

    // setenv("G_MESSAGES_DEBUG", "all", 1);

    init();

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

    int ret = start(vecUpdate);

    return EXIT_SUCCESS;
}

/**
 * @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;
}

/**
 * @brief 组装完整的ref
 *
 * @param id 扩展、运行时或应用 id
 * @param arch 架构
 * @param branch 分支名，如master，或从subsummary 获取的 versions 关键字的分支
 * @return string 返回完整的ref
 */
string KMUpdate::makeCompletedRef(string id, string arch, string branch)
{
    string completedRef = std::string("runtime/").append(id).append("/").append(arch).append("/").append(branch);
    return completedRef;
}

/**
 * @brief 根据ref获取运行时
 *
 * @param ref
 * @return std::string
 */
std::string KMUpdate::getRuntimeFromRef(KMCommonRef &ref)
{
    string runtimePref;
    if (ref.baseType == "runtime")
    {
        // 运行时 "runtime"
        runtimePref = getValueByGroupAndKeyFromKVData(ref.metadata, "Runtime", "runtime"); // top.openkylin.Platform/x86_64/0.5
        runtimePref = std::string("runtime/").append(runtimePref);
        kmlogger.info("appref:  %s group [Application] runtime: %s", ref.completedId.c_str(), runtimePref.c_str());
    }
    else
    {
        // 应用 app
        runtimePref = getValueByGroupAndKeyFromKVData(ref.metadata, "Application", "runtime"); // app/org.kde.kclock/x86_64/master
        runtimePref = std::string("runtime/").append(runtimePref);
        kmlogger.info("appref:  %s group [Application] runtime: %s", ref.completedId.c_str(), runtimePref.c_str());
    }

    return runtimePref;
}

/**
 * @brief 根据ref获取基本类型
 *
 * @param ref
 * @return std::string
 */
std::string KMUpdate::getBaseTypeFromRef(KMCommonRef &ref)
{

    if (KMUtil::startsWithForRef(ref.completedId, "app/"))
    {
        return "app";
    }
    else
    {
        return "runtime";
    }
}

/**
 * @brief 根据ref获取基本类型
 *
 * @param ref
 * @return std::string
 */
std::string KMUpdate::getBaseTypeFromRef(string ref)
{
    if (KMUtil::startsWithForRef(ref, "app/"))
    {
        return "app";
    }
    else
    {
        return "runtime";
    }
}

/**
 * @brief 获取需要安装的 ref.id
 *
 * @return std::string
 */
std::string KMUpdate::getDefaultId()
{
    return globalLastId;
}

/**
 * @brief 根据ref获取默认分支版本
 *
 * @param ref
 * @return std::string
 */
std::string KMUpdate::getDefaultAppVersionRef(KMCommonRef &ref)
{
    string defaultAppBranch = KMUtil::getLastPartWithDelimiter(ref.completedId);
    kmlogger.info("default App branch::  %s", defaultAppBranch.c_str());
    return defaultAppBranch;
}

/**
 * @brief 根据ref获取默认分支版本
 *
 * @param ref
 * @return std::string
 */
std::string KMUpdate::getDefaultRuntimeVersionRef(KMCommonRef &ref)
{
    string defaultRuntimeBranch = KMUtil::getLastPartWithDelimiter(ref.runtime);
    kmlogger.info("default runtime branch:  %s", defaultRuntimeBranch.c_str());
    return defaultRuntimeBranch;
}

/**
 * @brief 判断当前ref是否安装
 *
 * @param ref
 * @return true
 * @return false
 */
bool KMUpdate::currentRefIsInstalled(KMCommonRef &ref)
{
    KMCommonRef updateRef = makeRefDataFromInitRefString(ref.completedId);
    ref.baseType = getBaseTypeFromRef(ref.completedId);
    ref.id = updateRef.id;
    ref.arch = updateRef.arch;
    ref.branch = updateRef.branch;

    KMRef appRef;
    appRef.kind = getBaseTypeFromRef(ref.completedId);
    appRef.id = updateRef.id;
    appRef.arch = updateRef.arch;
    appRef.branch = updateRef.branch;
    appRef.refCommitId = ref.commit;
    // d->m_folder->setActive(appRef, ref.commit); // 避免 [error]: filesystem error: status: 符号连接的层数过多 [/opt/kaiming/app/org.kde.kclock/x86_64/master/active]

    kmlogger.debug("Type:%s id:%s arch:%s branch:%s commit:%s", appRef.kind.c_str(), appRef.id.c_str(), appRef.arch.c_str(), appRef.branch.c_str(), appRef.refCommitId.c_str());

    string matchRef = ref.baseType.append("/").append(ref.id).append("/").append(appRef.arch);
    kmlogger.info("matchRef: %s", matchRef.c_str());
    for (auto &localRef : d->m_localRefs)
    {
        kmlogger.info("first: %s", localRef.first.c_str());
        if (localRef.first.find(matchRef) != string::npos)
        {
            kmlogger.info("found first: %s", localRef.first.c_str());
            return true;
        }
    }

    return false;
}

/**
 * @brief 判断当前ref是否安装
 *
 * @param name ref 完整名称
 * @return true
 * @return false
 */
bool KMUpdate::currentRefStringIsInstalled(string name)
{
    kmlogger.info("matchRef: %s", name.c_str());
    for (auto &localRef : d->m_localRefs)
    {
        kmlogger.info("first: %s", localRef.first.c_str());
        if (localRef.first.find(name) != string::npos)
        {
            kmlogger.info("found first: %s", localRef.first.c_str());
            return true;
        }
    }
    return false;
}

/**
 * @brief 判断当前ref是否可升级
 *
 * @param name 软件包名,如 app/org.kde.kclock/x86_64/stable
 * @return true 可升级
 * @return false 不可升级
 */
bool KMUpdate::isUpgradable(string name)
{
    // Todo: Adjust which apps need to be updated
    kmlogger.info("开始对 %s 进行校验是否可更新", name.c_str());

    // 不安装,则不更新
    // Todo: 考虑使用 ostree api获取 deploy,然后进行id过滤
    bool isInstalled = currentRefStringIsInstalled(name);
    if (!isInstalled)
    {
        kmlogger.error("Not install ref: ", name.c_str());
        std::cout << _("Not install this ref ") << name << std::endl;
        return false;
    }

    kmlogger.info("开始在远程仓库中查找 ref 对应的最新 commitid ...");
    // runtime/top.openkylin.Platform/x86_64/{0.4, 0.5} to get: runtime/top.openkylin.Platform/x86_64
    string matchRef = KMUtil::removeLastPartWithDelimiter(name);
    kmlogger.info("Match pattern: %s", matchRef.c_str());

    vector<string> matchRefs;
    if (!d->m_remoteSummary->findRefVectorFromRemoteSummary(d->m_appRef.subsummary, NULL, matchRef, matchRefs))
    {
        kmlogger.error("No find this ref: %s", name.c_str());
        std::cout << _("Not find this ref ") << name << std::endl;
        return false;
    }
    kmlogger.info("Default ref name: %s", name.c_str());

    char *checksum = NULL;
    GBytes *metadataBytes = NULL;
    guint64 downloadSize = 0;
    guint64 installedSize = 0;
    const char *metadata = NULL;
    VarMetadataRef sparseCache;
    char *summaryChecksum = NULL;
    VarRefInfoRef info; // 每一个搜索的ref对应的info
    if (d->m_appRef.subsummary == NULL || !d->m_remoteSummary->summaryLookupRefCommitAndInfo(d->m_appRef.subsummary, NULL, name.c_str(), &checksum, &info))
    {
        kmlogger.error("Not found this ref commit : %s", name.c_str());
        return false;
    }

    string commit = string(checksum, checksum + strlen(checksum));
    kmlogger.info("Ref <completedId : commit>:  <%s : %s>", d->m_appRef.completedId.c_str(), commit.c_str());

    if (d->m_localRefs.find(d->m_appRef.completedId) == d->m_localRefs.end())
    {
        kmlogger.error("No find this ref: %s", name.c_str());
        std::cout << _("Not find this ref ") << name << std::endl;
        return false;
    }

    string localOldCommit = d->m_localRefs[d->m_appRef.completedId];
    kmlogger.info("name: %s localOldCommit: %s remoteNewCommit:%s", name.c_str(), localOldCommit.c_str(), commit.c_str());
    if (localOldCommit == commit)
    {
        // 前后 commit 一致,则不需要更新
        kmlogger.error("Has been installed this ref, not update: %s", name.c_str());
        return false;
    }
    kmlogger.info("commit 前后不一致,需要更新 %s", name.c_str());
    return true;
}

/**
 * @brief 判断当前ref是否可升级
 *
 * @param ref 软件包名,如 org.kde.kclock
 * @return true 可升级
 * @return false 不可升级
 */
int KMUpdate::isUpgradabled(string name)
{
    // Todo: Adjust which apps need to be updated
    kmlogger.info("开始对 %s 进行校验是否可更新", name.c_str());

    KMCommonRef commonRef = makeRefDataFromInitRefString(name);
    string completedId = commonRef.completedId;
    kmlogger.info("completedId: %s", completedId.c_str());

    // 不安装,则不更新
    // Todo: 考虑使用 ostree api获取 deploy,然后进行id过滤
    bool isInstalled = currentRefStringIsInstalled(name);
    if (!isInstalled)
    {
        kmlogger.error("Not install ref: ", name.c_str());
        std::cout << _("Not install this ref ") << name << std::endl;
        return KM_ERROR_NOT_INSTALLED;
    }

    kmlogger.info("开始在远程仓库中查找 ref 对应的最新 commitid ...");
    // runtime/top.openkylin.Platform/x86_64/{0.4, 0.5} to get: runtime/top.openkylin.Platform/x86_64
    string matchRef = KMUtil::removeLastPartWithDelimiter(name); // 对于运行时，有不同的版本信息，因此不能用完整的ref进行匹配，最多用 runtime/refId/arch 方式，不用加上branch
    // string matchRef = completedId; // 此处使用完整ref进行匹配，后续对于运行时如有调整可以修改
    kmlogger.info("Match pattern: %s", matchRef.c_str()); // this is emplty for system dbus

    string ostreeRepoUrl = d->m_ostreeHandler->getRemoteUrl(REPO_NAME);

    string summaryUrl = string(ostreeRepoUrl).append("/").append("summary.idx");
    d->m_remoteRepoName = std::string(REPO_NAME);
    string summaryIdxPath = d->m_folder->getSummariesIdxPath(d->m_remoteRepoName).u8string();

    // Curl 下载 summary.idx
    if (!d->m_remoteSummary->curlApiDownload(summaryUrl, summaryIdxPath))
    {
        kmlogger.error("Curl download failed. SummaryUrl:  %s  Store summary.idx to SummaryIdxPath: %s", summaryUrl.c_str(), summaryIdxPath.c_str());

        std::cout << _("Curl download summary file failed!") << std::endl;

        return KM_ERROR_CURL_DOWNLOAD_FAILED;
    }

    // 根据summary.idx 获取 index
    string index;
    if (!d->m_remoteSummary->parseSummaryIdxFromPath(summaryIdxPath, index))
    {
        kmlogger.error("Get commit from summary.idx failed!");
        return KM_ERROR_GET_SUMMARY_COMMIT_FAILED;
    }
    kmlogger.info("Get index from summary.idx :  %s", index.c_str());

    d->m_arch = d->m_util->kmGetArch();
    kmlogger.info("Arch:  %s", d->m_arch.c_str());

    d->m_repoIndexUrl = ostreeRepoUrl + string("/summaries/") + index + string(".gz");
    kmlogger.info("Need to curl .gz file :  %s", d->m_repoIndexUrl.c_str());

    d->m_repoIndexLocalGzPath = d->m_folder->getSummariesIndexGzPath(d->m_remoteRepoName, d->m_arch, index);
    kmlogger.info("Local .gz file :  %s", d->m_repoIndexLocalGzPath.c_str());

    if (!d->m_remoteSummary->curlApiDownloadBinaryGZFile(d->m_repoIndexUrl, d->m_repoIndexLocalGzPath))
    {
        kmlogger.error("Download .gz file failed.");

        std::cout << _("Curl download binary summary file failed!") << std::endl;

        return KM_ERROR_CURL_DOWNLOAD_FAILED;
    }

    d->m_repoIndexLocalSubPath = d->m_folder->getSummariesIndexGzSubPath(d->m_remoteRepoName, d->m_arch, index);
    kmlogger.info("Local .sub file :  %s", d->m_repoIndexLocalSubPath.c_str());

    // 获取仓库清单文件: kaiming-repo-x86_64-eda00c3fd4a65f6f2032d28e7cc0c623e5f073b0627d7208c13fe1a0fcd41ab4.sub
    if (!d->m_remoteSummary->decompressGzFile(d->m_repoIndexLocalGzPath, d->m_repoIndexLocalSubPath))
    {
        kmlogger.error("Decompression .gz file failed.");
        return KM_ERROR_DECOMPRESSION_FAILED;
    }

    GVariant *subsummary = d->m_remoteSummary->getSummaryFromPath(d->m_repoIndexLocalSubPath);
    if (!subsummary)
    {
        kmlogger.error("Get GVariant pointer data failed!");
        return KM_ERROR_GET_SUBSUMMARY_GVARIANT_FAILED;
    }

    if (!d->m_remoteSummary->findRefFromRemoteSummary(subsummary, NULL, matchRef))
    {
        kmlogger.error("No find this ref: %s", name.c_str());
        std::cout << _("Not find this ref ") << name << std::endl;
        return KM_ERROR_REF_NOT_FOUND; // 未找到ref对应的commit信息
    }
    kmlogger.info("Default ref name: %s", name.c_str());

    char *checksum = NULL;
    GBytes *metadataBytes = NULL;
    guint64 downloadSize = 0;
    guint64 installedSize = 0;
    const char *metadata = NULL;
    VarMetadataRef sparseCache;
    char *summaryChecksum = NULL;
    VarRefInfoRef info; // 每一个搜索的ref对应的info
    if (subsummary == NULL || !d->m_remoteSummary->summaryLookupRefCommitAndInfo(subsummary, NULL, completedId.c_str(), &checksum, &info))
    {
        kmlogger.error("Not found this ref commit : %s", completedId.c_str());
        return KM_ERROR_REF_COMMIT_NOT_FOUND;
    }

    string commit = string(checksum, checksum + strlen(checksum));
    kmlogger.info("Ref <completedId : commit>:  <%s : %s>", completedId.c_str(), commit.c_str());

    if (d->m_localRefs.find(completedId) == d->m_localRefs.end())
    {
        kmlogger.error("No find this ref: %s", name.c_str());
        std::cout << _("Not find this ref ") << name << std::endl;
        return KM_ERROR_REF_NOT_FOUND;
    }

    // string localOldCommit = d->m_localRefs[completedId]; // 存在多个commit时，ostree获取的不生效
    string localOldCommit;
    fs::path activePath = d->m_folder->getActivePath(name);
    kmlogger.info("activePath: %s", activePath.string().c_str());
    if (fs::exists(activePath) && fs::is_symlink(activePath))
    {
        localOldCommit = fs::read_symlink(activePath);
    }
    else
    {
        return KM_ERROR_REF_COMMIT_NOT_FOUND;
    }

    kmlogger.info("completedId: %s localOldCommit: %s remoteNewCommit:%s", completedId.c_str(), localOldCommit.c_str(), commit.c_str());
    if (localOldCommit == commit)
    {
        // 前后 commit 一致,则不需要更新
        kmlogger.error("Has been installed this ref, not update: %s", name.c_str());
        return KM_ERROR_NOT_NEED_UPDATE;
    }
    kmlogger.info("commit 前后不一致,需要更新 %s", name.c_str());
    return KM_ERROR_NO;
}

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

/**
 * @brief 测试调用的api
 *
 * @param name 开明包名，如org.kde.kclock
 * @param version 开明版本号，这里实际上是ostree branch版本，暂未细化到具体子版本。空代表最新版本，如"master"
 * @return true
 * @return false
 */
bool KMUpdate::testUpdate(string name, string version)
{
    int ret = false;
    vector<string> vecUpdate;

    d->m_appRef = makeRefDataFromInitRefString(name);
    d->m_isYes = true;
    vecUpdate.push_back(name);

    ret = start(vecUpdate);

    return ret;
}

/**
 * @brief 给软件商店调用的api
 *
 * @param name 开明包名，如org.kde.kclock
 * @param version 开明版本号，这里实际上是ostree branch版本，暂未细化到具体子版本。空代表最新版本，如"master"
 * @param updateCallback 进度回调函数
 * @param error
 * @return true
 */
int KMUpdate::update(string name, string version, int &error, UpdateCallBack updateCallback)
{
    g_updateProgressCallback = updateCallback;

    int ret = KM_ERROR_NO;
    vector<string> vecUpdate;

    d->m_appRef = makeRefDataFromInitRefString(name);
    d->m_isYes = true;
    vecUpdate.push_back(name);

    init();

    ret = start(vecUpdate);

    error = ret;

    return ret;
}

static const char *getFilePathCached(GFile *file)
{
    const char *path;
    static GQuark filePathQuark = 0;

    if (G_UNLIKELY(filePathQuark == 0))
    {
        filePathQuark = g_quark_from_static_string("kaiming-file-path");
    }

    do
    {
        path = (char *)g_object_get_qdata((GObject *)file, filePathQuark);
        if (path == NULL)
        {
            char *newFilePath = NULL;
            newFilePath = g_file_get_path(file);
            if (newFilePath == NULL)
            {
                return NULL;
            }

            if (g_object_replace_qdata((GObject *)file, filePathQuark, NULL, newFilePath, g_free, NULL))
            {
                path = KMUtil::stealPointer(&newFilePath);
            }
        }
    } while (path == NULL);

    return path;
}

/**
 * @brief 得到下载refs列表
 *
 * @param ref  待处理的ref
 * @param metadata
 * @return true
 * @return false
 */
bool KMUpdate::getDownloadRefsList(KMCommonRef &ref, char *metadata)
{
    GBytes *metadataBytes = NULL;
    metadataBytes = g_bytes_new(metadata, strlen(metadata));

    kmlogger.info("get resolvedMetadata, resolvedMetakey from metadata ");
    GKeyFile *metakey = g_key_file_new();
    if (!g_key_file_load_from_bytes(metakey, metadataBytes, G_KEY_FILE_NONE, NULL))
    {
        kmlogger.info("get resolvedMetadata, resolvedMetakey from metadata ");
        return false;
    }
    ref.resolvedMetadata = g_bytes_ref(metadataBytes);
    ref.resolvedMetakey = KMUtil::stealPointer(&metakey);

    //  (const char *)g_bytes_get_data (metadataBytes)
    char *runtimeCompleteData = NULL;
    runtimeCompleteData = g_key_file_get_string(ref.resolvedMetakey, "Application", "runtime", NULL);
    kmlogger.info("%s is app, from group [Application] to find runtime: %s", ref.completedId.c_str(), runtimeCompleteData);

    g_auto(GStrv) groups = NULL;
    groups = g_key_file_get_groups(ref.resolvedMetakey, NULL);
    for (int i = 0; groups[i] != NULL; i++)
    {
        char *taggedExtension;
        if (g_str_has_prefix(groups[i], "Extension ") && *(taggedExtension = (groups[i] + strlen("Extension "))) != 0)
        {
            kmlogger.debug("search groups: %s ", groups[i]);

            char *extension = NULL;
            char *version = g_key_file_get_string(ref.resolvedMetakey, groups[i], "version", NULL);
            g_auto(GStrv) versions = g_key_file_get_string_list(ref.resolvedMetakey, groups[i], "versions", NULL, NULL);
            bool subdirectories = g_key_file_get_boolean(ref.resolvedMetakey, groups[i], "subdirectories", NULL);
            bool noAutodownload = g_key_file_get_boolean(ref.resolvedMetakey, groups[i], "no-autodownload", NULL);
            char *downloadIf = g_key_file_get_string(ref.resolvedMetakey, groups[i], "download-if", NULL);
            char *autopruneUnless = g_key_file_get_string(ref.resolvedMetakey, groups[i], "autoprune-unless", NULL);
            bool autodelete = g_key_file_get_boolean(ref.resolvedMetakey, groups[i], "autodelete", NULL);
            bool localeSubset = g_key_file_get_boolean(ref.resolvedMetakey, groups[i], "locale-subset", NULL);

            /* Parse actual extension name */
            parseExtensionWithTag(taggedExtension, &extension, NULL);
            kmlogger.debug("extension:  %s", extension);

            const char *defaultBranches[] = { NULL, NULL };
            const char **branches;
            int branchInt;

            if (versions)
            {
                branches = (const char **)versions;
            }
            else
            {
                if (version)
                {
                    kmlogger.info("version branch:  %s", version);
                    defaultBranches[0] = version;
                }
                else
                {
                    // 跟随runtime运行时的版本
                    defaultBranches[0] = ref.defaultRuntimeBranch.c_str();
                    kmlogger.info("default branch:  %s", defaultBranches[0]);
                }
                branches = defaultBranches;
            }

            for (branchInt = 0; branches[branchInt] != NULL; branchInt++)
            {
                char *checksum = NULL;
                const char *branch = branches[branchInt];
                kmlogger.info("extension: %s branch: %s", extension, branch);

                string completeRef = makeCompletedRef(string(extension), d->m_appRef.arch, string(branch));
                kmlogger.info("completedRef:  %s", completeRef.c_str());

                // completeRef = "runtime/org.freedesktop.Platform.GL.default/x86_64/22.08";
                char *runtimeChecksum = NULL;
                VarRefInfoRef info;
                if (d->m_remoteSummary->summaryLookupRefCommitAndInfo(d->m_appRef.subsummary, NULL, completeRef.c_str(), &runtimeChecksum, &info))
                {
                    kmlogger.info("add_related=> runtime ref:%s commitid: %s", completeRef.c_str(), runtimeChecksum);

                    KMCommonRef updateRef;
                    updateRef.remote = REPO_NAME;
                    updateRef.info = info;
                    updateRef.subsummary = d->m_appRef.subsummary;
                    updateRef.completedId = completeRef;
                    updateRef.commit = string(runtimeChecksum);

                    kmlogger.info("runtime ref info=> id:  %s commit: %s", updateRef.completedId.c_str(), updateRef.commit.c_str());

                    const char *metadata = NULL;

                    if (!d->m_remoteSummary->summaryRemoteStateLookupCache(updateRef.subsummary, updateRef.completedId.c_str(), &updateRef.downloadSize, &updateRef.installedSizeInt, &metadata))
                    {
                        // 获取当前 ref 的相关信息
                        kmlogger.error("Not get info from this ref  %s", updateRef.completedId.c_str());
                        return false;
                    }

                    updateRef.applicationInfo = string(metadata, metadata + strlen(metadata));

                    kmlogger.debug("runtime ref id:   %s applicationInfo: %s", updateRef.completedId.c_str(), updateRef.applicationInfo.c_str());

                    d->m_refDownloadList.push_back(updateRef);
                }
                else if (subdirectories)
                {
                    vector<string> subrefRefs = d->m_remoteSummary->summaryMatchSubrefs(ref.subsummary, nullptr, completeRef.c_str(), extension, d->m_appRef.arch.c_str(), branch);

                    for (const std::string &subrefRef : subrefRefs)
                    {
                        // kmlogger.info("1 add_related=>  subdirectories subrefRef: %s",  subrefRef);
                        char *subrefChecksum = NULL;
                        VarRefInfoRef subrefInfo;
                        if (d->m_remoteSummary->summaryLookupRefCommitAndInfo(ref.subsummary, nullptr, subrefRef.c_str(), &subrefChecksum, &subrefInfo))
                        {
                            kmlogger.info("add_related=>  subdirectories subrefRef: %s checksum:%s ", subrefRef.c_str(), subrefChecksum);

                            KMCommonRef updateSubRef;
                            updateSubRef.remote = REPO_NAME;
                            updateSubRef.subsummary = ref.subsummary;
                            updateSubRef.completedId = subrefRef;
                            updateSubRef.commit = string(subrefChecksum);
                            updateSubRef.info = subrefInfo;

                            const char *metadata = NULL;

                            if (!d->m_remoteSummary->summaryRemoteStateLookupCache(updateSubRef.subsummary, updateSubRef.completedId.c_str(), &updateSubRef.downloadSize, &updateSubRef.installedSizeInt, &metadata))
                            {
                                // 获取当前 ref 的相关信息
                                kmlogger.error("Not get info from this ref  %s", updateSubRef.completedId.c_str());
                                return false;
                            }

                            updateSubRef.applicationInfo = string(metadata, metadata + strlen(metadata));

                            kmlogger.debug("runtime ref id:   %s applicationInfo: %s", updateSubRef.completedId.c_str(), updateSubRef.applicationInfo.c_str());

                            d->m_refDownloadList.push_back(updateSubRef);
                        }
                    }
                }
            }
        }
    }

    return true;
}

void KMUpdate::parseExtensionWithTag(const char *extension, char **name, char **tag)
{
    const char *tagExtensionChar = strchr(extension, '@');

    if (tagExtensionChar)
    {
        if (name != NULL)
        {
            *name = g_strndup(extension, tagExtensionChar - extension);
        }

        /* Everything after the @ */
        if (tag != NULL)
        {
            *tag = g_strdup(tagExtensionChar + 1);
        }

        return;
    }

    if (name != NULL)
    {
        *name = g_strdup(extension);
    }

    if (tag != NULL)
    {
        *tag = NULL;
    }
}

/**
 * @brief 创建一个 export/bin/ref.id shell脚本，写入执行内容，并赋予0755权限
 *
 * @param fileName
 * @param ref
 * @return true
 * @return false
 */
bool KMUpdate::createExportBinShellScript(KMCommonRef ref, string fileName)
{
    // 设置内容
    string shellHead = "#!/bin/sh";
    string content = shellHead.append("\nexec ").append("/usr/bin/kaiming run --branch=").append(ref.branch).append(" --arch=").append(ref.arch).append(" ").append(ref.id).append(" \"$@\"\n");

    kmlogger.info("export bin bash content:\n%s", content.c_str());
    // 创建文件并写入内容
    std::ofstream file(fileName);
    if (file.is_open())
    {
        file << content;
        file.close();
    }
    else
    {
        kmlogger.error("Failed to open file:  %s", fileName.c_str());
        return false;
    }

    // 设置文件权限为 0755
    if (chmod(fileName.c_str(), 0755) != 0)
    {
        kmlogger.error("Failed to set file permission for:  %s", fileName.c_str());
        return false;
    }

    return true;
}

/**
 * @brief 检查文件是否存在，如果不存在则创建文件并输入空字符串
 *
 * demo:
 *       新建 "active/files/.ref"，并输入空字符串 ""
 *
 * @param filename
 */
void KMUpdate::createFileForEmptyString(const std::string &filename)
{
    // 打开文件进行检查
    std::ifstream inputFile(filename);
    if (inputFile.is_open())
    {
        kmlogger.error("File already exists:  %s", filename.c_str());
        inputFile.close();
        return;
    }

    // 创建文件并打开输出流
    std::ofstream outputFile(filename);
    if (!outputFile.is_open())
    {
        kmlogger.error("Failed to create file:  %s", filename.c_str());
        return;
    }

    // 写入空字符串到文件
    outputFile << "";

    // 关闭文件
    outputFile.close();
}

/**
 * @brief 当文件不存在时，创建文件
 *
 * @param filename
 */
void KMUpdate::createFileIfNotExist(const std::string &filename)
{
    fs::path filePath(filename);

    if (!fs::exists(filePath))
    {
        // 文件不存在，创建文件
        std::ofstream file(filename);
        if (file.good())
        {
            kmlogger.info("File created successfully.");
        }
        else
        {
            kmlogger.error("Failed to create file.");
        }
    }
    else
    {
        kmlogger.info("File already exists.");
    }
}

/**
 * @brief 读取 .desktop 文件内容
 *
 * @param filename
 * @return std::vector<std::string>
 */
std::vector<std::string> KMUpdate::getDataFromKVFile(const std::string &filename)
{
    std::vector<std::string> lines;
    std::ifstream file(filename);
    if (file.is_open())
    {
        std::string line;
        while (std::getline(file, line))
        {
            lines.push_back(line);
        }
        file.close();
    }
    else
    {
        kmlogger.error("Failed to open file::  %s", filename.c_str());
    }
    return lines;
}

/**
 * @brief 修改 .desktop 文件内容
 *
 * @param filename
 * @param newLines
 * @return true
 * @return false
 */
bool KMUpdate::setDataToKVFile(const std::string &filename, const std::vector<std::string> &newLines)
{
    std::ofstream file(filename);
    if (file.is_open())
    {
        for (const auto &line : newLines)
        {
            file << line << std::endl;
        }
        file.close();
        return true;
    }
    else
    {
        kmlogger.error("inFailed to open file for writingdex:  %s", filename.c_str());
        return false;
    }
}

/**
 * @brief 读取 键值对文件(如.desktop, .service) 中具体字段的值
 *
 * @param lines
 * @param key
 * @return std::string
 */
std::string KMUpdate::getValueByKeyFromKVData(const std::vector<std::string> &lines, string key)
{
    for (const auto &line : lines)
    {
        if (line.find(key) != std::string::npos)
        {
            return line.substr(key.size()); // 去掉 "Exec=" 的前缀，返回剩余的部分
        }
    }
    return ""; // 没有找到 key 字段，返回空字符串
}

/**
 * @brief 接受组名和键值对的 key，然后从字符串中提取对应的值
 *
 * @param metadata 待搜索的字符串
 * @param groupName 组名
 * @param key 关键字key
 * @return std::string
 */
std::string KMUpdate::getValueByGroupAndKeyFromKVData(const std::string &metadata, const std::string &groupName, const std::string &key)
{
    std::istringstream iss(metadata);
    std::string line;

    // 逐行读取 metadata
    while (std::getline(iss, line))
    {
        // 查找 [groupName] 开始的组
        size_t startPos = line.find(groupName);
        if (startPos != std::string::npos)
        {
            // 找到了组，继续读取键值对
            std::unordered_map<std::string, std::string> keyValuePairs;

            while (std::getline(iss, line) && !line.empty() && line[0] != '[')
            {
                size_t eqPos = line.find('=');
                if (eqPos != std::string::npos)
                {
                    std::string key = line.substr(0, eqPos);
                    std::string value = line.substr(eqPos + 1);
                    keyValuePairs[key] = value;
                }
            }

            // 在当前组中查找键值对的值
            if (!keyValuePairs.empty())
            {
                auto it = keyValuePairs.find(key);
                if (it != keyValuePairs.end())
                {
                    return it->second;
                }
            }
        }
    }

    return ""; // 未找到对应值
}

/**
 * @brief 获取当前目录下的所有文件
 *
 * 对于获取service文件有用
 *
 * @param directoryPath
 * @param files
 * @return true
 * @return false
 */
bool KMUpdate::getAllFilesInDirectory(const std::string &directoryPath, std::vector<std::string> &files)
{
    try
    {
        // 清空文件列表
        files.clear();

        // 遍历目录中的文件
        for (const auto &entry : fs::directory_iterator(directoryPath))
        {
            // 将文件路径添加到文件列表中
            files.push_back(entry.path().string());
        }

        return true; // 成功获取文件列表
    }
    catch (const std::exception &e)
    {
        kmlogger.error("Failed to get files: %s", e.what());
        return false; // 获取文件列表失败
    }
}

/**
 * @brief 更改service文件
 *
 * @param ref
 * @param serviceFileDirectoryPath
 */
void KMUpdate::modifyServiceFile(KMCommonRef &ref, string serviceFileDirectoryPath)
{
    std::vector<std::string> files;

    bool noModifyServiceFileFlag = false;

    // 调用函数获取目录下所有文件
    bool success = getAllFilesInDirectory(serviceFileDirectoryPath, files);
    if (success)
    {
        for (const auto &file : files)
        {
            if (file.find(".service") != std::string::npos)
            {
                string serviceFileName = file;
                kmlogger.info("serviceFileName:  %s", serviceFileName.c_str());

                std::vector<std::string> serviceDatas = getDataFromKVFile(serviceFileName);

                for (auto &line : serviceDatas)
                {
                    // 修改 Exec 字段
                    size_t eqPos = line.find("Exec=");
                    if (eqPos != std::string::npos)
                    {
                        std::string value = line.substr(eqPos + strlen("Exec="));
                        if (value.find(" run") != std::string::npos)
                        {
                            // 第一次已进行修改，因此不修改当前文件
                            noModifyServiceFileFlag = true;
                        }

                        string serviceExec = "Exec=";
                        string content = serviceExec.append("/usr/bin/kaiming run ").append("--branch=").append(ref.branch).append(" ").append("--arch=").append(ref.arch).append(" ").append("--command=").append(value).append(" ").append(ref.id);
                        line = content;
                    }
                }

                if (!noModifyServiceFileFlag)
                {
                    // 将修改后的内容写回 .service 文件
                    if (setDataToKVFile(serviceFileName, serviceDatas))
                    {
                        kmlogger.info("Desktop file modified successfully.");
                    }
                }
            }
        }
    }
    else
    {
        kmlogger.error("Failed to get files in directory.");
    }
}

/**
 * @brief 更改desktop文件，比如修改Exec, Icon, X-Kaiming
 *
 * @param ref
 * @param exportDesktopFilePath
 */
void KMUpdate::modifyDesktopFile(KMCommonRef &ref, string exportDesktopFilePath)
{
    std::vector<std::string> desktopDatas = getDataFromKVFile(exportDesktopFilePath);

    /* Add a marker so consumers can easily find out that this launches a sandbox */
    bool needAddKaimingTag = false;
    bool noModifyDesktopFileFlag = false;

    // 修改需要的字段
    for (auto &line : desktopDatas)
    {
        // 修改 Exec 字段
        size_t eqPos = line.find("Exec=");
        if (eqPos != std::string::npos)
        {
            std::string value = line.substr(eqPos + strlen("Exec="));
            if (value.find(" run") != std::string::npos)
            {
                // 第一次已进行修改，因此不修改当前文件
                noModifyDesktopFileFlag = true;
            }

            string formatExec = d->m_util->formatExecString(value, ref.id);
            kmlogger.info("formatExec: %s", formatExec.c_str());

            string serviceExec = "Exec=";
            string content = serviceExec.append("/usr/bin/kaiming run ").append("--branch=").append(ref.branch).append(" ").append("--arch=").append(ref.arch).append(" ").append("--command=").append(formatExec);
            line = content;
        }

        // 修改 Icon 字段
        if (line.find("Icon=") != std::string::npos)
        {
            // Todo: Modify Icon
            // line = "Icon=/path/to/new/icon";
        }

        // 新增 X-Type 字段
        if (line.find("X-Kaiming=") != std::string::npos)
        {
            needAddKaimingTag = false;
        }
        else
        {
            needAddKaimingTag = true;
        }
    }

    if (noModifyDesktopFileFlag)
    {
        return;
    }

    if (needAddKaimingTag)
    {
        string kaimingTag = "X-Kaiming=" + ref.id;
        desktopDatas.push_back(kaimingTag);
    }

    // 将修改后的内容写回 .desktop 文件
    if (!setDataToKVFile(exportDesktopFilePath, desktopDatas))
    {
        kmlogger.info("Desktop file modified failed.");
    }
}

/**
 * @brief 递归遍历exports/目录并处理文件，比如创建软链接
 *
 * @param ref
 * @param srcDir
 * @param destDir
 */
void KMUpdate::modifyExportsDirectory(KMCommonRef &ref, const std::string &srcDir, const std::string &destDir)
{

    for (const auto &entry : fs::directory_iterator(srcDir))
    {
        if (fs::is_directory(entry))
        {
            // 递归处理子目录
            std::string subSrcDir = entry.path().string();
            std::string subDestDir = destDir + "/" + entry.path().filename().string();
            fs::create_directory(subDestDir);
            modifyExportsDirectory(ref, subSrcDir, subDestDir);
        }
        else
        {
            // 处理文件
            std::string srcFilePath = entry.path().string();
            std::string destFilePath = destDir + "/" + entry.path().filename().string();
            kmlogger.info("srcFilePath:%s destFilePath:%s", srcFilePath.c_str(), destFilePath.c_str());

            if (fs::exists(fs::path(destFilePath)))
            {
                continue;
            }

            if (srcFilePath.find(".desktop") != std::string::npos || srcFilePath.find(".service") != std::string::npos || srcFilePath.find(".svg") != std::string::npos || srcFilePath.find(".png") != std::string::npos)
            {

                // 如果是 .desktop, .service, .svg 文件，则创建相对路径软链接到目标目录

                // 获取 srcFilePath 相对于 destFilePath 的相对路径
                std::string relativePath = fs::relative(fs::path(srcFilePath), fs::path(destFilePath).parent_path()).string();
                kmlogger.info("Export .desktop, .service, .svg, .png relativePath:  %s", relativePath.c_str());

                fs::create_symlink(fs::path(relativePath), fs::path(destFilePath));
            }
            else if (srcFilePath.find("export/bin") != std::string::npos)
            {
                // 如果是 export/bin 二进制文件，则创建相对路径软链接到目标目录
                std::string relativePath = fs::relative(fs::path(srcFilePath), fs::path(destFilePath).parent_path()).string();
                kmlogger.info("Export bin relativePath:  %s", relativePath.c_str());

                fs::create_symlink(fs::path(relativePath), fs::path(destFilePath));
            }
        }
    }
}

/**
 * @brief 接受不定数量的 cacheShellPath 参数，并将它们拼接到命令中，然后执行命令
 *
 * 作用是生成应用desktop, icon, service等缓存
 *
 * @param cacheShellPaths
 */
void KMUpdate::runMultiperTriggerUpdateCacheCommand(const vector<string> &cacheShellPaths)
{
    for (const auto &path : cacheShellPaths)
    {
        runTriggerUpdateCacheCommand(path);
    }
}

/**
 * @brief 接受不定数量的 cacheShellPath 参数，并将它们拼接到命令中，然后执行命令
 *
 *
 * @param cacheShellPaths
 */
void KMUpdate::runTriggerUpdateCacheCommand(const std::string &cacheShellPath)
{
    // 使用 std::ostringstream 拼接命令
    std::ostringstream commandStream;
    commandStream << KMUtil::getKMBox() << " --unshare-ipc --unshare-net --unshare-pid --ro-bind / / --proc /proc --dev /dev --bind "
                  << " /opt/kaiming /opt/kaiming " << cacheShellPath << " /opt/kaiming";

    std::string command = commandStream.str();

    // 执行命令
    int result = std::system(command.c_str());
    if (result != 0)
    {
        kmlogger.error("Failed to execute command:  %s", command.c_str());
    }
}

/**
 * @brief  判断当前类型是否为整形类型
 *
 * @param str
 * @return true
 * @return false
 */
bool KMUpdate::isInteger(const std::string &str)
{
    try
    {
        // 尝试将字符串转换为整数
        int num = std::stoi(str);
        // 没有抛出异常，则转换成功，返回 true
        return true;
    }
    catch (const std::invalid_argument &)
    {
        // 抛出 std::invalid_argument 异常，表示字符串不是一个有效的整数
        return false;
    }
    catch (const std::out_of_range &)
    {
        // 抛出 std::out_of_range 异常，表示转换后的整数超出了可表示的范围
        return false;
    }
}

/**
 * @brief 格式化输出下载的包信息，运行时输出是下载大小，软件包是安装大小
 *
 * @param ref
 * @param index
 */
void KMUpdate::printRefInfo(KMCommonRef &ref, int index)
{
    string installedSize;
    if (ref.baseType == "app")
    {
        installedSize = formatFileSize((double)ref.installedSizeInt);
    }
    else
    {
        installedSize = formatFileSize((double)ref.downloadSize);
    }

    ref.installedSize = installedSize;

    printf("%-5s %-50s %-20s %-10s %-20s %-20s\n", std::to_string(index).c_str(), ref.id.c_str(), ref.branch.c_str(), "u", ref.remote.c_str(), installedSize.c_str());
}

/**
 * @brief 格式化文件大小
 *
 * @param size
 * @return string
 */
string KMUpdate::formatFileSize(double size)
{
    vector<string> units = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
    int index = 0;

    while (size >= 1024.0 && index < units.size())
    {
        size /= 1024.0;
        index++;
    }

    std::ostringstream oss;
    oss << std::fixed << std::setprecision(1) << size << " " << units[index];
    return oss.str();
}
