/* src/common/KMStorageDir.h
 *
 * Copyright (C) 2023 OpenKylin, Inc
 */

#ifndef __KMDIR_H__
#define __KMDIR_H__

#include <ostree.h>

#include <map>
#include <memory>
#include <string>
#include <vector>

#include "KMCommonTypePrivate.h"
#include "KMContext.h"
#include "KMFolder.h"
#include "KMInfoJson.h"
#include "KMMetaData.h"
#include "kmtranslation.h"

struct KMDeploy
{
    KMRef m_ref;
    std::string m_dir;
    std::shared_ptr<KMInfoJson> m_infoJson;

    // TODO : 下面成员废弃
    std::shared_ptr<KMMetaData> m_metadata;
    std::shared_ptr<KMContext> m_systemOverrides;
    std::shared_ptr<KMContext> m_systemAppOverrides;
    OstreeRepo *m_repo;
};

/**
 * @brief : 主要用于封装kaiming包存储目录的相关操作
 */
class KMStorageDir
{
public:
    KMStorageDir();
    KMStorageDir(const KMStorageDir &other);
    ~KMStorageDir();

    /**
     * @brief : 获取开明包系统级安装的KMStorageDir对象
     * @return: 开明包系统级安装的KMStorageDir对象
     */
    static KMStorageDir getSystemDefaultDir();

    /**
     * @brief : 获取开明包用户安装目录KMStorageDir对象
     * @return: 开明包用户用户安装目录KMStorageDir对象
     */
    static KMStorageDir getUserDir();

    /**
     * @brief : 获取KMStorageDir对象base路径
     */
    std::string getBasePath();

    /**
     * findInstalledRef:
     * @brief : 根据入参查找匹配的已经安装的app或sdk、runtime信息
     * @param : [in] appName，app或sdk等的id，形如"org.videolan.VLC"或"org.freedesktop.Sdk"
     * @param : [in] branch，版本
     * @param : [in] arch，架构
     * @param : [in] kinds，查找开明包安装目录的类型，app还是runtime等
     * @return: 返回匹配的已安装的app或sdk/runtime等的开明包信息，未找到则返回的KMRef对象的id、arch、branch等信息为空串
     */
    KMRef findInstalledRef(const std::string &appName, const std::string &branch, const std::string &arch, KaimingKinds kinds);

    /**
     * findFuzzyMatchingRef:
     * @brief : 根据入参在已安装的app或sdk/runtime等的开明包信息列表中查找匹配的开明包信息
     * @param : [in] appName，app或sdk等的id，形如"org.videolan.VLC"或"org.freedesktop.Sdk"
     * @param : [in] kinds，查找开明包安装目录的类型，app还是runtime等
     * @param : [in] refs，已安装的app或sdk/runtime等的开明包信息列表
     * @return: 返回匹配的已安装的开明包信息，未找到则返回的KMRef对象的id、arch、branch等信息为空串
     */
    std::vector<KMRef> findFuzzyMatchingRef(const std::string &appName, KaimingKinds kinds, const std::vector<KMRef> &refs);

    /**
     * getAllInstalledRefs:
     * @brief : 根据kinds查找已经安装的app或sdk/runtime的开明包信息
     * @param : [in] kinds，查找开明包安装目录的类型，app还是runtime等
     * @return: 返回已安装的app或sdk/runtime等的开明包信息列表
     */
    std::vector<KMRef> getAllInstalledRefs(KaimingKinds kinds);

    /**
     * listDeployedRefs:
     * @brief : 根据入参查找匹配的已经安装的app或sdk、runtime信息
     * @param : [in] kinds，查找开明包安装目录的类型，app还是runtime等
     * @param : [in] namePrefix，app或sdk等的id的前缀，形如"org.videolan.VLC."或"org.freedesktop.Sdk."
     * @param : [in] arch，架构
     * @param : [in] branch，版本
     * @return: 返回已安装的app或sdk/runtime等的开明包信息列表
     */
    std::vector<KMRef> listDeployedRefs(KaimingKinds kinds, const std::string &namePrefix, const std::string &arch, const std::string &branch);

    /**
     * @brief : 判断KMStorageDir对象是否空对象
     */
    explicit operator bool() { return d.use_count() > 0; }

    /**
     * listRefs:
     * @brief : 根据kinds查找已经安装的app或sdk/runtime的开明包信息列表
     * @param : [in] kinds，查找开明包安装目录的类型，app还是runtime等
     * @return: 返回已安装的app或sdk/runtime等的开明包信息列表
     */
    std::vector<KMRef> listRefs(KaimingKinds kinds);

    /**
     * loadDeployed:
     * @brief : 根据ref信息查询开明包是否部署
     * @param : [in] ref, 开明包基础信息
     * @return: 返回部署信息
     */
    std::shared_ptr<KMDeploy> loadDeployed(const KMRef &ref);

    /**
     * @brief : 获取未维护的扩展包目录
     * @param : [in] ref，扩展包的ref
     * @return: 未维护的扩展包目录
     */
    std::string getUnmaintainedExtensionDir(const KMRef &ref);
    std::string findUnmaintainedExtensionDirIfExists(const KMRef &ref);

    void addExtension(const KMExtension &ext, std::vector<KMExtension> &list);

    /**
     * @brief 确保仓库存在
     * @param allow_empty 是否允许为空
     * @return true 存在 false 不存在
     */
    bool ensureRepo(bool allow_empty);

public:
    /**
     * @brief : 获取开明包系统级安装的base目录
     * @return: 开明包系统级安装的base目录
     */
    static std::string getSystemDefaultBaseDirLocation();

    /**
     * @brief : 获取开明包用户数据base目录
     * @return: 开明包用户数据base目录
     */
    static std::string getUserBaseDirLocation();

    /**
     * @brief : 获取用户数据目录
     * @return: 用户数据目录
     */
    static std::string getUserDataDirLocation();

    /**
     * @brief : 获取应用数据目录
     * @param : [in] id，应用id
     * @return: 数据目录
     */
    static std::string getDataDir(const std::string &id);

    /**
     * listRefs:
     * @brief : 根据目录名（系统安装目录下的app或runtime下的子目录名）对应的目录树信息判断已经安装的开明包信息列表
     * @param : [in] baseDir，系统安装目录下的app或runtime目录
     * @param : [in] kinds，查找开明包安装目录的类型，app还是runtime等
     * @param : [in] name，系统安装目录下的app或runtime下的子目录名
     * @param : [out] refs，已经安装的开明包信息列表
     * @return: 是否存在
     */
    static bool listRefs(const fs::path &baseDir, KaimingKinds kind, std::vector<KMRef> &refs);

    /**
     * findMatchingRef:
     * @brief : 根据入参在已安装的app或sdk/runtime等的开明包信息列表中查找匹配的开明包信息
     * @param : [in] appName，app或sdk等的id，形如"org.videolan.VLC"或"org.freedesktop.Sdk"
     * @param : [in] branch，版本
     * @param : [in] arch，架构
     * @param : [in] kinds，查找开明包安装目录的类型，app还是runtime等
     * @param : [in] refs，已安装的app或sdk/runtime等的开明包信息列表
     * @return: 返回匹配的已安装的开明包信息，未找到则返回的KMRef对象的id、arch、branch等信息为空串
     */
    static KMRef findMatchingRef(const std::string &appName, const std::string &branch, const std::string &arch, KaimingKinds kinds, const std::vector<KMRef> &refs);

    /**
     * @brief : 根据开明包ref信息获取开明包安装的base目录
     * @param : [in] ref, 开明包ref信息
     * @return: 开明包安装的base目录
     * @note  : 目前开明包仅支持系统级安装
     */
    static std::string getRefBaseDirLocation(const KMRef &ref);

    /**
     * getDeployedDir:
     * @brief : 根据ref信息获取开明包部署路径
     * @param : [in] ref, 开明包基础信息
     * @param : [out] ref, 会更新里面的commit信息（主要是将active>具体的commitid）
     * @return: 开明包部署路径
     */
    static std::string getDeployedDir(const KMRef &ref);

    /**
     * collectDeployedRefs:
     * @brief : 根据目录名（系统安装目录下的app或runtime下的子目录名）对应的目录树信息判断已经安装的开明包信息列表
     * @param : [in] baseDir，系统安装目录下的app或runtime目录
     * @param : [in] kinds，查找开明包安装目录的类型，app还是runtime等
     * @param : [in] namePrefix，app或sdk等的id的前缀，形如"org.videolan.VLC."或"org.freedesktop.Sdk."
     * @param : [in] arch，架构
     * @param : [in] branch，版本
     * @return: 返回已安装的app或sdk/runtime等的开明包信息列表
     */
    static std::vector<KMRef> collectDeployedRefs(const fs::path &baseDir, KaimingKinds kind, const std::string &namePrefix, const std::string &arch, const std::string &branch);

    /**
     * extensionMatchesReason:
     * @brief : 扩展${extensionId}是否与${reasons}匹配
     * @param : [in] extensionId,
     * @param : [in] reasons
     * @param : [in] defaultValue, ${reasons}为空时默认是否匹配
     * @return: true-匹配；false-不匹配
     */
    static bool extensionMatchesReason(const std::string &extensionId, const std::string &reasons, bool defaultValue);

private:
    KMStorageDir(const std::string &path, bool user);

    class Private;
    std::shared_ptr<Private> d;
};

#endif // !__KMDIR_H__
