#ifndef KMOSTREEHANDLER_H
#define KMOSTREEHANDLER_H

// #include <glib-2.0/glib/gvariant.h> // Exist compire error, so replace it with <glib.h>
#include <glib.h>
#include <ostree-1/ostree.h>

#include <filesystem>
#include <functional>
#include <map>
#include <memory>
#include <string>

#include "KMUtil.h"

using namespace std;
namespace fs = std::filesystem;

typedef struct _ProgressStatus
{
    string ref;
    string lastRef;

    /* Extra data information */
    guint64 outstandingExtraData;
    guint64 totalExtraData;
    guint64 transferredExtraDataBytes;
    guint64 totalExtraDataBytes; /* the sum of all extra data file sizes (in bytes) */

    char *ostreeStatus; /* only sent by OSTree when the pull ends (with or without an error) */
    guint64 startTime;
    guint64 bytesTransferred;       /* every and all transferred data (in bytes) */
    guint64 fetchedDeltaPartSize; /* the size (in bytes) of already fetched static deltas */
    guint64 totalDeltaPartSize;   /* the total size (in bytes) of static deltas */

    guint outstandingWrites; /* all missing writes (sum of outstanding content, metadata and delta writes) */
    uint32_t fetched;         /* sum of content + metadata fetches */
    uint32_t requested;       /* sum of requested content + metadata fetches */

    guint outstandingMetadataFetches; /* missing metadata-only fetches */
    guint metadataFetched;             /* the number of fetched metadata objects */
    guint totalDeltaParts;

    /* Self-progress-reporting fields, not from OSTree */
    guint progress;
    guint lastTotal;

    /* Flags */
    guint estimating : 1;
    guint lastWasMetadata : 1;
    guint done : 1;
    guint reportedOverflow : 1;

} ProgressStatus;

/**
 * @brief ostree hander
 */
class KMOSTreeHandler
{
public:
    KMOSTreeHandler(const string &path);
    ~KMOSTreeHandler();

    typedef function<void(ProgressStatus progressStatus)> CallBack;

    /**
     * @brief 根据 ref 的完整id 获取最新的 commit
     *
     * @param refCompletedId ref 的完整id
     * @return string 最新的 commit
     */
    string readLatestCommit(const string &refCompletedId);

    /**
     * @brief 得到 commitData 数据
     *
     * @param commit
     * @return GVariant*
     */
    GVariant *getCommitData(const string &commit);

    /**
     * @brief 加载 commit 数据
     *
     * @param commit
     * @return true
     * @return false
     */
    bool loadCommit(const string &commit);

    fs::path getRepoPath();
    fs::path getRootPath(const string &commit);

    /**
     * @brief 是否是extra-data
     */
    bool isExtraData(const string &commit);

    /**
     * @brief 获取OstreeRepo
     */
    OstreeRepo *getRepo();

    /**
     * @brief 设置collection—_id
     * @param remote 名称
     * @param collectionId collection_id名称
     * @return bool ture 是 false 否
     */
    bool setCollectionId(const string &remote, const string &collectionId);

    /**
     * @brief 获取collection_id
     * @param remote
     * @param collectionId
     * @return true 成功 false 失败
     */
    bool getCollectionId(const string &remote, string &collectionId);

    /**
     * @brief 获取 commit 时间戳
     *
     * @param commit
     * @return uint64_t
     */
    uint64_t getCommitTimestamp(const string &commit);

    /**
     * @brief 获取 remote 地址
     *
     * @param remoteName
     * @return string
     */
    string getRemoteUrl(const string &remoteName);

    /**
     * @brief 获取 remote 对应已安装的所有 app 和 runtime
     *
     * @param remoteName
     * @return map<string, KMRef>:
     *  key：commit
     *  value：ref 结构体
     */
    map<string, KMRef> getLocalRefs(const string &remoteName);

    /**
     * @brief 获取 remote 对应已安装并部署的所有 app 和 runtime
     *
     * 此接口比较准确判断当前软件包是否已成功安装 : 一切以 deploy/** 为准
     *
     * @param remoteName
     * @return map<string, KMRef>:
     *  key：commit
     *  value：ref 结构体
     */
    map<string, KMRef> getLocalRefsDeployed(const string &remoteName);

    /**
     * @brief 获取 remote 对应已安装并部署的所有 app 和 runtime
     *
     * 此接口比较准确判断当前软件包是否已成功安装 : 一切以 deploy/** 为准
     *
     * 对于更新来说,一般都是通过 <completedId, commit> 来进行匹配
     *
     * @param remoteName
     * @return map<string, string>:
     *  key：completedId
     *  value：commit
     */
    map<string, string> getLocalRefsDeployedMap(const string &remoteName);

    /**
     * @brief 获取源中所有 app 和 runtime
     *
     * @param remoteName
     * @return map<string, KMRef>:
     *  key：commit
     *  value：ref 结构体
     */
    map<string, KMRef> getRemoteRefs(const string &remoteName);

    /**
     * @brief 使用参数的数据同步
     * @param remoteName
     * @param ref_name
     * @param collection_id
     * @param commit
     * @param refs
     * @param cb 进度回调
     * @return true 成功 false 失败
     */
    bool pullWithOptions(const string &remoteName, const string &ref, GVariant *opts, CallBack cb);

    // 检查文件或目录权限，并修复 setgid 位
    bool fixPermissions(const std::string &path);

    /**
     * @brief 数据同步，同步之后数据为压缩格式，执行完成后需要 checkout，同步函数
     *
     * @param remoteName
     * @param refs
     * @param cb 进度回调
     * @return true
     * @return false
     */
    bool pull(const string &remoteName, vector<string> refs, CallBack cb);

    /**
     * @brief 检出，同步函数
     *
     * @param commit
     * @return true
     * @return false
     */
    bool checkout(const string &commit, const string &tmpTargetPath, const string &subPath = "");

    /**
     * @brief 创建 deploy ref, 为了避免 deployed commits being pruned (pull --no-deploy)
     *
     * ostree lookup: ostree refs /opt/kaiming/repo |grep deploy
     * deploy ref formats: deploy/app/org.kde.kclock/x86_64/master
     *
     * @param completedRef 完整的ref，如 app/org.kde.kclock/x86_64/master
     * @param commit ref对应的commit, 如 f3b1314b51f2b8865089f13cbae5b7cd449b169f0b36530d27da74beb68aa492
     * @return true
     * @return false
     */
    bool updateDeployRef(const string &completedRef, const string &commit);

    /**
     * @brief 从 repo 中删除 ref 标签，没有真实删除数据，需要执行 repoPrune 同步删除数据
     *
     * @param remoteName
     * @param ref
     * @param commit
     * @return true
     * @return false
     */
    bool deleteRefTag(const string &remoteName, const string &ref, const string &commit = "");

    /**
     * @brief ostree repo 删除数据
     *
     * @return true
     * @return false
     */
    bool repoPrune();

    void updateProgress();
    static string checksumFromBytes(const u_char *sha265_bytes);

private:
    void initProgress();
    bool checkCommit(const string &commit);

private:
    class Private;
    std::unique_ptr<Private> d;
};

#endif