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

#include "common/KMLogger.h"
#include "common/KMDirUtils.h"
#include "common/KMConfigData.h"
#include "common/KMCrun.h"
#include "common/KMDbusProxyCall.h"

/**
 * @brief 再次运行应用
 * @param appid 应用ID
 * @param commandArgs 运行命令及参数
 * @param sockPath 套接字路径
 * @return true 成功 false 失败
 */
bool KMRunnerOnce::run(const std::string &appid, const std::string &commandArgs, const std::string &sockPath, const std::vector<std::string> &envs)
{
    std::string containerid;
    if (!isAlreadyRun(appid, containerid))
    {
        KMDebug("Application is not running: " + appid);
        return false;
    }

    std::string cmd;
    if (!assembleRunOnceCmd(containerid, commandArgs, sockPath, envs, cmd))
    {
        return false;
    }

    if (!runCmd(cmd, appid, containerid))
    {
        return false;
    }

    return true;
}

/**
 * @brief 应用是否已运行
 * @return true 成功 false 失败
 */
bool KMRunnerOnce::isAlreadyRun(const std::string &appid, std::string &containerid)
{
    if (appid.empty())
    {
        return false;
    }
    //获取运行状态
    std::tuple<bool, std::string> reply;
    KMDbusProxyCall dbusCall("org.kaiming.proxy", "/org/kaiming/proxy", "org.kaiming.proxy.system");
    GVariant *var = g_variant_new("(ss)", appid.c_str(), std::to_string(getuid()).c_str());
    if (!dbusCall.methodCallSync("getRunStatus", var, "(bs)", reply, true))
    {
        KMError("getRunStatus failed.");
        return false;
    }
    auto [status, cid] = reply;
    containerid = cid;
    return status;
}

/**
 * @brief 运行命令
 * @param cmd 运行命令及参数
 * @param appid 应用名
 * @param cid 容器id
 * @return true 成功 false 失败
 */
bool KMRunnerOnce::runCmd(const std::string &cmd, const std::string &appid, const std::string &cid)
{
    std::tuple<int, std::string> reply;
    GVariant *var = g_variant_new("(sss)", cmd.c_str(), appid.c_str(), cid.c_str());
    KMDbusProxyCall dbusCall("org.kaiming.proxy", "/org/kaiming/proxy", "org.kaiming.proxy.system");
    if (!dbusCall.methodCallSync("run", var, "(is)", reply, true))
    {
        KMError("run dbus call failed");
        return false;
    }

    auto [reti, msg] = reply;
    if (reti != 0)
    {
        KMError("[CRUN_ERROR]: " + msg);
        return false;
    }

    return true;
}

/**
 * @brief 组装二次运行命令
 * @param containerid 容器id
 * @param commandArgs 运行命令及参数
 * @param sockPath 套接字路径
 * @param envs 环境变量
 * @param cmd 输出的命令字符串
 * @return true 成功 false 失败
 */
bool KMRunnerOnce::assembleRunOnceCmd(const std::string &containerid, const std::string &commandArgs, const std::string &sockPath, const std::vector<std::string> &envs, std::string &cmd)
{
    if (commandArgs.empty() || containerid.empty())
    {
        KMError("run command or containerid is empty");
        return false;
    }

    cmd.append("exec --detach");
    for (const auto &env : envs)
    {
        cmd.append(" --env=").append(env);
    }

    if (!sockPath.empty())
    {
        cmd.append(" --tty ");
        cmd.append(" --console-socket=").append(sockPath);
    }

    if (std::string cwdDir; KMDirUtils::getCwdDir(cwdDir))
    {
        cmd.append(" --cwd=").append(cwdDir);
    }

    cmd.append(" ").append("--user=").append(std::to_string(getuid())).append(":").append(std::to_string(getgid()));
    cmd.append(" ").append(containerid).append(" ").append(commandArgs);

    return true;
}