#include "KMList.h"


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

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

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

/**
 * @brief list命令行选项
 */
class KMList::Options : public KMOptions
{
public:
    Options() = default;
    virtual ~Options() = default;

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

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

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

    /**
     * @brief 命令行参数检查项
     */
    void checkOptions(const opt::variables_map &varsMap);

public:
    bool m_bHelp;
    string m_sideload;
    string m_arch;
    string m_kind;
};

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

/**
 * @brief 命令行参数解析后操作
 */
void KMList::Options::postParseHook()
{
    checkOptions(m_varsMap);

    if (m_bHelp)
    {
        showUsage();
        exit(EXIT_SUCCESS);
    }
}

/**
 * @brief 命令行参数添加描述选项
 */
void KMList::Options::addOptions()
{
    m_optionsDescription = std::make_unique<opt::options_description>(_("\nUsage:\n \tkaiming list [OPTION…] - List all installed applications "));

    opt::options_description helpOptions(_("Help Options"));
    helpOptions.add_options()("help,h", _("Show help options"));

    opt::options_description appOptions(_("Application Options"));
    appOptions.add_options()("arch=ARCH", opt::value<string>(&m_arch), _("Specify architecture"));
    appOptions.add_options()("kind=KIND", opt::value<string>(&m_kind), _("Specify type, such as app, runtime, base, extension"));

    m_optionsDescription->add(helpOptions).add(appOptions);
}

/**
 * @brief 命令行参数检查项
 */
void KMList::Options::checkOptions(const opt::variables_map &varsMap)
{
    checkOption(varsMap, "help", "h", m_bHelp);
}

class KMList::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<KMFormatter> m_formatter; // 格式化输出

    map<string, KMRef> m_localRefs;        // 本地安装的软件包列表
    list<KMLocalInfoList> m_localInfoList; // 所有显示的本地信息
};

REGISTER_SUBCOMMAND_DYNCREATE(list, KMList)

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

KMList::~KMList() { }

/**
 * @brief list 命令参数处理,不需要参数
 *
 * 列出所有已安装的软件包信息,只支持不含参数
 *
 * 使用实例:
 *      1. kaiming list
 *
 * @param argc
 * @param argv
 * @return int
 */
int KMList::despose(int argc, char **argv)
{
    vector<string> vecList;
    for (int i = 1; i < argc; i++)
    {
        if (argv[i][0] != '-')
        {
            vecList.push_back(argv[i]);
            continue;
        }
        else if (vecList.size() > 0)
        {
            kmlogger.error("args error");
            return KM_ERROR_INVALID_ARGS;
        }
    }

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

    init();

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

    if (vecList.size() > 0)
    {
        kmlogger.error("Args number error, not support args.");
        printf("%s\n", _("Args number error, not support args."));
        return KM_ERROR_INVALID_ARGS;
    }
    vecList.clear();

    int ret = start(vecList);

    return EXIT_SUCCESS;
}

/**
 * @brief 列出所有的已安装软件包
 *
 *
 * @param vecName
 * @return int
 */
int KMList::start(const vector<string> &vecName)
{
    if (vecName.empty())
    {
        kmlogger.info("Args is empty, so list all packages.");

        d->m_formatter->setColumnWidth(35, 35, 15, 25, 15, 18, 30, 15);
        d->m_formatter->setShowArch(true);
        d->m_formatter->setShowChannel(true);
        d->m_formatter->printHeader();

        fs::path layersPath = d->m_folder->getLayersPath();

        findInfoJsonFiles(layersPath,"info.json", layersPath);

        d->m_formatter->printFooter();
    }

    return true;
}

void KMList::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_formatter = shared_ptr<KMFormatter>(new KMFormatter());
}

/**
 * @brief 给软件商店调用的api
 *
 * @param name 开明包名，如org.kde.kclock
 * @param version 开明版本号，这里实际上是ostree branch版本，暂未细化到具体子版本。空代表最新版本，如"master"
 * @param error
 * @return true
 * @return false
 */
list<KMLocalInfoList> KMList::getLocalInfoList(string name)
{
    list<KMLocalInfoList> localInfoList;
    KMLocalInfoList localInfo;

    localInfo.id = name;
    localInfoList.push_back(localInfo);

    return localInfoList;
}

/**
 * @brief 格式化输出下载的包信息，运行时输出是下载大小，软件包是安装大小
 *
 * @param ref
 * @param index
 */
void KMList::printRefInfo(KMLocalInfoList localInfo)
{
    printf("%-25s %-50s %-20s %-10s %-20s %-10s %-80s\n", localInfo.name.c_str(), localInfo.id.c_str(), localInfo.channel.c_str(), localInfo.module.c_str(), localInfo.version.c_str(), formatFileSize(localInfo.size).c_str(), localInfo.description.c_str());
}

/**
 * @brief 格式化文件大小
 *
 * @param size
 * @return string
 */
std::string KMList::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();
}

void KMList::findInfoJsonFiles(const fs::path& dirPath, const std::string& targetFilename, const std::string &destDir) 
{ 
    KMInfoJson infoJson;
    for (const auto &dirEntry : fs::directory_iterator(dirPath))
    {   
        bool founInfojson = false; // 标记是否在当前目录层找到info.json

        KMEntry localInfo;
        KMLocalInfoList localList;

        if (fs::is_symlink(fs::path(dirEntry)))
        {
            continue;
        }

        if (fs::is_directory(dirEntry)) 
        {  
            // 递归处理子目录
            std::string subSrcDir = dirEntry.path().string();
            std::string subDestDir = destDir + "/" + dirEntry.path().filename().string();
            fs::create_directory(subDestDir);
            
            // 过滤 "entires", "files" 目录，避免过深遍历或遍历到 bluetooth 这种 700权限目录导致读取权限有问题
            if (subSrcDir.find("/entries") != std::string::npos || subSrcDir.find("/files") != std::string::npos)
            {
                continue;
            }

            // 如果是目录，则递归调用  
            findInfoJsonFiles(subSrcDir, targetFilename, subDestDir);  
        }
        else if (dirEntry.is_regular_file() && dirEntry.path().filename() == targetFilename)
        {
            founInfojson = true; // 找到info.json，标记为true
            fs::path infoJsonPath = dirEntry.path();

            // 调用 loadFile() 函数解析info.json文件
            infoJson.loadFile(infoJsonPath);

            // 获取信息
            localInfo.id = infoJson.id;
            localInfo.channel = infoJson.channel;
            localInfo.module = infoJson.module;
            localInfo.version = infoJson.version;
            localInfo.size = formatFileSize(infoJson.size);
            localInfo.name = infoJson.name;
            localInfo.description = infoJson.description; 
            std::ostringstream oss;
            for (size_t i = 0; i < infoJson.archs.size(); ++i) 
            {
                oss << infoJson.archs[i];
                if (i != infoJson.archs.size() - 1) 
                {
                    oss << ", ";  // 分隔符
                }
            }
            localInfo.arch = oss.str();
            localList.kind = infoJson.kind;
            localList.archs = infoJson.archs;
            
            // 获取当前语言环境，输出对应语言环境下的name和description
            std::string localeName = KMUtil::getCurrentLocale();
            localInfo.name = !infoJson.names["name[" + localeName + "]"].empty() ? infoJson.names["name[" + localeName + "]"] : infoJson.name;
            KMUtil::removeTrailingNewline(localInfo.name);
            localInfo.description = !infoJson.descriptions["description[" + localeName + "]"].empty() ? infoJson.descriptions["description[" + localeName + "]"] : infoJson.description;
            KMUtil::removeTrailingNewline(localInfo.description);

            // 使用--kind --arch参数时过滤，只输出匹配的应用
            bool matchArch = d->m_options->m_arch.empty() || std::find(localList.archs.begin(), localList.archs.end(), d->m_options->m_arch) != localList.archs.end();
            // bool matchArch = d->m_options->m_arch.empty() || (localInfo.arch.find(d->m_options->m_arch) != std::string::npos);
            bool matchKind = d->m_options->m_kind.empty() || localList.kind == d->m_options->m_kind;
            if (matchArch && matchKind)
            {
                d->m_formatter->printEntry(localInfo);
            }
            break;
        }
    }
}