#include "KMBuilderGlobal.h"

KMBuilderGlobal::KMBuilderGlobal()
{
    m_pConfigData = builderContext.getBuilderConfig()->getConfigData();
}

KMBuilderGlobal::~KMBuilderGlobal()
{
}

vector<shared_ptr<KMBuildOptions>> KMBuilderGlobal::getAllBuildOptions()
{
    vector<shared_ptr<KMBuildOptions>> options;
    shared_ptr<KMBuildOptions> globalOptions = m_pConfigData->buildOptions;
    string arch = KMBuilderUtils::getArch();

    if (globalOptions)
    {
        options.push_back(globalOptions);
    }
    return options;
}

string KMBuilderGlobal::builderOptionsGetAppendedPath(string pathList, int type)
{
    vector<shared_ptr<KMBuildOptions>> options = getAllBuildOptions();

    if (pathList.empty())
    {
        return "";
    }

    for (auto option : options)
    {
        string append;
        string prepend;

        switch (type)
        {
        case 1:
            append = option->m_appendLdLibraryPath;
            prepend = option->m_prependLdLibraryPath;
            break;
        case 2:
            append = option->m_appendPkgConfigPath;
            prepend = option->m_prependPkgConfigPath;
            break;
        case 3:
            append = option->m_appendPath;
            prepend = option->m_prependPath;
            break;
        }

        if (!append.empty())
        {
            pathList + ":" + append;
        }

        if ((!prepend.empty()))
        {
            pathList + ":" + prepend;
        }
    }

    return pathList;
}

vector<string> KMBuilderGlobal::builderOptionsGetBuildArgs()
{
    vector<shared_ptr<KMBuildOptions>> options = getAllBuildOptions();
    vector<string> array;

    for (auto option : options)
    {
        vector<string> buildArgs = option->m_buildArgs;

        if (buildArgs.size())
        {
            for (auto arg : buildArgs)
            {
                array.push_back(arg);
            }
        }
    }

    if (builderContext.isSandbox())
    {
        if (array.size() > 0)
        {
            km_logger.error("Can't specify build-args in sandboxed build");
            array.clear();
            return array;
        }
        /* 无论出于何种原因，如果应用程序在元数据中具有网络访问权限，那么如果我们正在构建沙盒，请明确取消该权限 */
        array.push_back("--unshare=network");
    }
    return array;
}

vector<string> KMBuilderGlobal::builderOptionsGetEnv()
{
    vector<shared_ptr<KMBuildOptions>> options = getAllBuildOptions();

    vector<string> envs;
    string cflags, cppflags, cxxflags, ldflags;

    setEnvSDE(envs);

    cflags = buildOptionsGetFlags(1);
    if (!cflags.empty())
    {
        setenv("CFLAGS", cflags.c_str(), 0);
        envs.push_back(cflags);
    }

    cppflags = buildOptionsGetFlags(2);
    if (!cppflags.empty())
    {
        setenv("CPPFLAGS", cppflags.c_str(), 0);
        envs.push_back(cppflags);
    }

    cxxflags = buildOptionsGetFlags(3);
    if (!cxxflags.empty())
    {
        setenv("CXXFLAGS", cxxflags.c_str(), 0);
        envs.push_back(cxxflags);
    }

    ldflags = buildOptionsGetFlags(4);
    if (!ldflags.empty())
    {
        setenv("LDFLAGS", ldflags.c_str(), 0);
        envs.push_back(ldflags);
    }

    for (auto option : options)
    {
        map<string, string> opEnvs = option->m_env;
        if (!opEnvs.empty())
        {
            for (auto env : opEnvs)
            {
                string line = env.second;
                string eq;
                size_t pos = 0;
                pos = line.find('=');
                if (pos < line.length())
                {
                    eq = line.substr(pos);
                    string key = line.substr(pos);
                    string value = line.substr(pos + 1, line.size() - pos - 1);
                    setenv(key.c_str(), value.c_str(), 1);
                    envs.push_back(value);
                }
                else
                {
                    unsetenv(line.c_str());
                    // 使用std::remove移动要删除的元素到vector末尾，并返回新的逻辑结尾位置
                    auto new_end = std::remove(envs.begin(), envs.end(), line);
                    // 使用erase方法移除逻辑结尾位置之后的所有元素
                    envs.erase(new_end, envs.end());
                }
            }
        }
    }

    builderContextExtendEnvPost(envs);

    builderOptionsUpdatePath(1, envs);
    builderOptionsUpdatePath(2, envs);
    builderOptionsUpdatePath(3, envs);

    return envs;
}

void KMBuilderGlobal::builderOptionsUpdatePath(int type, vector<string> &envs)
{
    string path = "";
    string old = "";

    switch (type)
    {
    case 1:
        old = getenv("LD_LIBRARY_PATH");
        if (old.empty())
        {
            old = "/app/lib";
        }
        break;
    case 2:
        old = getenv("PKG_CONFIG_PATH");
        if (old.empty())
        {
            old = "/app/lib/pkgconfig:/app/share/pkgconfig:/usr/lib/pkgconfig:/usr/share/pkgconfig";
        }
        break;
    case 3:
        old = getenv("PATH");
        break;
    }

    path = builderOptionsGetAppendedPath(old, type);
    if (!path.empty())
    {
        switch (type)
        {
        case 1:
            setenv("LD_LIBRARY_PATH", path.c_str(), 1);
            break;
        case 2:
            setenv("PKG_CONFIG_PATH", path.c_str(), 1);
            break;
        case 3:
            setenv("PATH", path.c_str(), 1);
            break;
        }
        envs.push_back(path);
    }
}

void KMBuilderGlobal::setEnvSDE(vector<string> &envs)
{
    int64_t overrideSourceDateEpoch = builderContext.getOverrideSourceDateEpoch();
    if (overrideSourceDateEpoch != 0)
    {
        string sde = KMBuilderUtils::argSplice("%", overrideSourceDateEpoch);
        setenv("SOURCE_DATE_EPOCH", sde.c_str(), 0);
        envs.push_back(sde);
    }
}

void KMBuilderGlobal::builderContextExtendEnvPost(vector<string> &envs)
{
    string path = "";
    string ccacheDir = "";

    path = getenv("PATH");
    if (path.empty())
    {
        path = "/app/bin:/usr/bin";
    }

    if (builderContext.isCcache())
    {
        path = KMBuilderUtils::argSplice("/run/ccache/bin:", path);
        ccacheDir = "/run/ccache";
    }
    else
    {
        ccacheDir = "/run/ccache/disabled";
    }

    setenv("CCACHE_DIR", ccacheDir.c_str(), 1);
    setenv("PATH", path.c_str(), 1);
    envs.push_back(ccacheDir);
    envs.push_back(path);
}

void KMBuilderGlobal::builderModuleCleanupCollect(shared_ptr<KMBuilderModule> builderModule, bool platform, std::unordered_map<std::string, bool> &toRemoveHT)
{
    vector<string> globalPatterns;
    vector<string> localPatterns;

    const std::vector<std::string> &changedFiles = builderModule->getChanges();
    if (changedFiles.empty())
        return;

    if (platform)
    {
        globalPatterns = m_pConfigData->cleanupPlatform;
        localPatterns = builderModule->getData()->cleanupPlatform;
    }
    else
    {
        globalPatterns = m_pConfigData->cleanup;
        localPatterns = builderModule->getData()->cleanup;
    }

    for (string path : changedFiles)
    {
        string unprefixedPath;
        string prefix;

        string tempStrFiles = "files/";
        string tempStrUsr = "usr/";
        if (!path.compare(0, tempStrFiles.size(), tempStrFiles))
        {
            prefix = "files/";
        }
        else if (!path.compare(0, tempStrUsr.size(), tempStrUsr))
        {
            prefix = "usr/";
        }
        else
            continue;

        unprefixedPath = path.substr(prefix.size());

        collectCleanupForPath(globalPatterns, unprefixedPath, prefix, toRemoveHT);
        collectCleanupForPath(localPatterns, unprefixedPath, prefix, toRemoveHT);

        string tempStrLDebug = "lib/debug/";
        string tempStrDebug = ".debug/";
        if (!path.compare(0, tempStrLDebug.size(), tempStrLDebug) && !path.compare(path.size() - tempStrDebug.size() - 1, path.size() - 1, tempStrDebug))
        {
            string realPath = unprefixedPath;
            string realParent = "";
            string parent = "";
            string debugPath = unprefixedPath.substr(strlen("lib/debug/"));
            debugPath[debugPath.size() - strlen(".debug")] = '\0';

            while (true)
            {
                if (matchesCleanupForPath(globalPatterns, debugPath) || matchesCleanupForPath(localPatterns, debugPath))
                {
                    // 拼接键（这里我们使用 std::string 的 + 运算符来拼接字符串）
                    std::string key = (!prefix.empty() ? prefix : "") + realPath;
                    // 值设置为 true（用于标记key存在）
                    toRemoveHT[key] = true;
                }

                realParent = KMBuilderUtils::getDirname(realPath);
                if (realParent.compare("."))
                {
                    break;
                }
                realPath = realParent;
                parent = KMBuilderUtils::getDirname(debugPath);
                debugPath = parent;
            }
        }
    }
}

bool KMBuilderGlobal::matchesCleanupForPath(vector<string> patterns, string path)
{
    if (patterns.empty())
    {
        return false;
    }

    for (auto pattern : patterns)
    {
        if (KMBuilderUtils::matchesPathPattern(path, pattern))
        {
            return true;
        }
    }
    return false;
}

void KMBuilderGlobal::collectCleanupForPath(vector<string> patterns, string path, string addPrefix, std::unordered_map<std::string, bool> &toRemoveHT)
{
    if (patterns.empty())
    {
        return;
    }

    if (path.find("pkgconfig") != std::string::npos)
    {
        cout << "path:" << path << endl;
    }

    for (auto pattern : patterns)
    {
        kaimingCollectMatchesForPathPattern(path, pattern, addPrefix, toRemoveHT);
    }
}

void KMBuilderGlobal::kaimingCollectMatchesForPathPattern(string path, string pattern, string addPrefix, std::unordered_map<std::string, bool> &toRemoveHT)
{
    string match, rest;
    if (pattern[0] != '/')
    {
        match = KMBuilderUtils::pathMatchPrefix(pattern, KMBuilderUtils::getBasename(path));
        if (match.empty())
        {
            return;
        }
        rest = path.substr(match.size() - 1, path.size());
        if (!rest.empty())
        {
            // 拼接键（这里我们使用 std::string 的 + 运算符来拼接字符串）
            std::string key = (!addPrefix.empty() ? addPrefix : "") + path;
            // 值设置为 true（用于标记key存在）
            toRemoveHT[key] = true;
        }
    }
    else
    {
        //有多重路徑，目錄以及子目錄
        match = KMBuilderUtils::pathMatchPrefix(pattern, path);
        if (match.empty())
        {
            return;
        }
        rest = path.substr(match.size(), path.size());
        if (!rest.empty())
        {
            //先存储直接文件路径
            std::string key = (!addPrefix.empty() ? addPrefix : "") + path;
            toRemoveHT[key] = true;
        }
        while (!rest.empty())
        {
            //cout << "rest:" << rest << endl;
            std::string::size_type pos = 0;
            std::string::size_type rest_pos = 0;
            pos = path.find(rest);
            string prefix = path.substr(0, pos);
            // 拼接键（这里我们使用 std::string 的 + 运算符来拼接字符串）
            std::string key = (!addPrefix.empty() ? addPrefix : "") + prefix;
            // 值设置为 true（用于标记key存在）
            toRemoveHT[key] = true;

            while (rest_pos < rest.size() && rest[rest_pos] == '/')
            {
                rest_pos++;
            }
            // 如果没有更多斜杠，则退出循环
            if (rest_pos >= rest.size())
            {
                break;
            }

            if ((rest_pos = rest.find('/', rest_pos)) != std::string::npos)
            {
                rest = rest.substr(0, rest_pos);
            }
            else
            {
                rest = "";
            }
        }
    }
}

string KMBuilderGlobal::buildOptionsGetFlags(int type)
{
    vector<shared_ptr<KMBuildOptions>> options = getAllBuildOptions();

    string sdkFlags;
    switch (type)
    {
    case 1:
        sdkFlags = builderContext.getBuilderSdkConfig()->m_cFlags;
        break;
    case 2:
        sdkFlags = builderContext.getBuilderSdkConfig()->m_cppFlags;
        break;
    case 3:
        sdkFlags = builderContext.getBuilderSdkConfig()->m_cxxFlags;
        break;
    case 4:
        sdkFlags = builderContext.getBuilderSdkConfig()->m_ldFlags;
        break;
    }

    if (sdkFlags.empty())
    {
        return "";
    }

    for (auto option : options)
    {
        string flag = option->m_cFlags;
        bool override = option->m_cflagsOverride;

        switch (type)
        {
        case 1:
            flag = option->m_cFlags;
            override = option->m_cflagsOverride;
            break;
        case 2:
            flag = option->m_cppFlags;
            override = option->m_cppFlagsOverride;
            break;
        case 3:
            flag = option->m_cxxFlags;
            override = option->m_cxxFlagsOverride;
            break;
        case 4:
            flag = option->m_ldFlags;
            override = option->m_ldFlagsOverride;
            break;
        }

        if (override && !sdkFlags.empty())
        {
            sdkFlags.clear();
        }

        if (!flag.empty())
        {
            if (!sdkFlags.empty())
            {
                sdkFlags += " ";
            }
            sdkFlags += flag;
        }
    }

    return sdkFlags;
}

string KMBuilderGlobal::configGetVariousId(string suffix)
{
    string id = KMBuilderUtils::replaceIdPrefix(std::any_cast<string>(m_pConfigData->attributeMap["id"]));
    return (id + suffix);
}

std::string KMBuilderGlobal::appId()
{
    return std::any_cast<std::string>(m_pConfigData->attributeMap["id"]);
}

string KMBuilderGlobal::configGetVariousIdPlatform(string suffix)
{
    string id = "";
    string idPlatform = std::any_cast<string>(m_pConfigData->attributeMap["id-platform"]);
    if (!idPlatform.empty())
    {
        string id = KMBuilderUtils::replaceIdPrefix(std::any_cast<string>(m_pConfigData->attributeMap["id-platform"]));
        return (id + suffix);
    }
    return id;
}

string KMBuilderGlobal::configGetBranch()
{
    string branchConfig = std::any_cast<string>(m_pConfigData->attributeMap["branch"]);
    if (!branchConfig.empty())
    {
        return branchConfig;
    }
    string branchContext = builderContext.getDefaultBranch();
    if (!branchContext.empty())
    {
        return branchContext;
    }

    string branchConfigDefault = std::any_cast<string>(m_pConfigData->attributeMap["default-branch"]);
    if (!branchConfigDefault.empty())
    {
        return branchConfigDefault;
    }

    return DEFAULT_BRANCH;
}

string KMBuilderGlobal::kaimingComposeRef(bool app, string name, string branch, string arch)
{
    if (app)
        return kaimingBuildAppRef(name, branch, arch);
    else
        return kaimingBuildRuntimeRef(name, branch, arch);
}

string KMBuilderGlobal::kaimingBuildAppRef(string app, string branch, string arch)
{
    if (branch.empty())
        branch = DEFAULT_BRANCH;

    if (arch.empty())
        arch = KMBuilderUtils::getArch();

    string start = "app";
    string fullPath(start + "/" + app + "/" + arch + "/" + branch);
    return fullPath;
}

string KMBuilderGlobal::kaimingBuildRuntimeRef(string runtime, string branch, string arch)
{
    if (branch.empty())
        branch = DEFAULT_BRANCH;

    if (arch.empty())
        arch = KMBuilderUtils::getArch();

    string start = "runtime";
    string fullPath(start + "/" + runtime + "/" + arch + "/" + branch);

    return fullPath;
}

bool KMBuilderGlobal::cpAll(string src, string dest, string keepInToplevel, KaimingCpFlags flags, vector<string> skipFiles)
{
    bool ret = false;
    vector<string> enumerators;
    string src_info = "";
    string destChild = "";
    int dest_dfd = -1;
    bool merge = (flags & KAIMING_CP_FLAGS_MERGE) != 0;
    bool no_chown = (flags & KAIMING_CP_FLAGS_NO_CHOWN) != 0;
    bool move = (flags & KAIMING_CP_FLAGS_MOVE) != 0;
    string child_info = "";

    //获取子路径下所有文件以及目录
    enumerators = KMBuilderUtils::enumerateDirectoryPath(src);
    if (enumerators.empty())
    {
        return ret;
    }
    try
    {
        // 尝试创建目录
        fs::create_directories(dest);
        std::cout << "Directory created successfully.\n";
    }
    catch (const fs::filesystem_error &e)
    {
        // 处理任何文件系统错误
        std::cerr << "Failed to create directory: " << e.what() << '\n';
    }

    //合并
    if (!merge)
    {
        return ret;
    }

    /* 合并时，请确保新目录位于顶层内部，而不是外部的符号链接 */
    if (!keepInToplevel.empty() && !fileIsIn(dest, keepInToplevel))
    {
        std::cerr << "递归复制超出目标界限" << std::endl;
        return ret;
    }

    //更改权限和用户组
    if (!no_chown)
    {
        if (!cpUID(src, dest))
        {
            return ret;
        }
    }

    if (!cpMod(src, dest))
    {
        return ret;
    }

    for (auto enumerator : enumerators)
    {
        string name = enumerator.substr(src.length() + 1, enumerator.length() - src.length() - 1);
        string srcChild = enumerator;
        bool skip = false;

        for (auto skipFile : skipFiles)
        {
            if (name == skipFile)
            {
                skip = true;
                break;
            }
        }

        destChild = dest + string("/") + name;
        std::error_code ec;
        if (skip)
        {
            /* skip src */
        }
        else if (fs::exists(srcChild) && fs::is_directory(srcChild))
        {
            if (!cpAll(srcChild, destChild, keepInToplevel, flags, skipFiles))
                return ret;
        }
        else
        {
            // 删除目标文件（如果存在）
            if (fs::exists(destChild, ec))
            {
                if (fs::remove(destChild, ec) && ec)
                {
                    km_logger.info("旧文件删除失败!");
                    std::cerr << "Error: " << ec.message() << std::endl;
                    return ret;
                }
            }

            // 设置复制选项（在C++中没有直接的标志对应，但可以通过逻辑来控制）
            fs::copy_options options = fs::copy_options::overwrite_existing; // 覆盖现有文件
            bool preserve_permissions = !no_chown;                           // 是否保留权限（类似于所有权和权限的元数据）

            // 根据 move 变量的值选择复制或移动
            if (move)
            {
                try
                {
                    fs::rename(srcChild, destChild);
                    std::cout << "Renamed successfully.\n";
                }
                catch (const fs::filesystem_error &e)
                {
                    std::cerr << "Failed to rename: " << e.what() << '\n';
                    return ret;
                }
            }
            else
            {
                try
                {
                    fs::copy(srcChild, destChild);
                    std::cout << "cp successfully.\n";
                }
                catch (const fs::filesystem_error &e)
                {
                    std::cerr << "Failed to cp: " << e.what() << '\n';
                    return ret;
                }
            }
        }
    }

    std::error_code ec;
    if (move && fs::remove_all(src, ec) && ec)
    {
        km_logger.info("src文件删除失败!");
        std::cerr << "Error: " << ec.message() << std::endl;
        return ret;
    }

    ret = true;
    return ret;
}

//确认toplevel是否被包含于file
bool KMBuilderGlobal::fileIsIn(string file, string toplevel)
{
    string canonicalFile = "";
    string canonicalToplevel = "";

    canonicalToplevel = canonicalizeFile(toplevel);
    if (canonicalToplevel.empty())
    {
        return false;
    }

    canonicalFile = canonicalizeFile(file);
    if (canonicalFile.empty())
    {
        return false;
    }

    bool bEqual = (canonicalFile == canonicalToplevel);
    bool bPrefix = (canonicalFile.find(canonicalToplevel) == 0);

    return (bEqual || bPrefix);
}

//确认文件存在且获取规范唯一路径
string KMBuilderGlobal::canonicalizeFile(string path)
{
    string canonicalPath = "";

    std::error_code ec;
    canonicalPath = std::filesystem::canonical(path, ec);

    if (!ec)
    {
        std::cout << "Canonical path: " << canonicalPath << std::endl;
    }
    else
    {
        std::cerr << "Error: " << ec.message() << std::endl;
    }

    return canonicalPath;
}

bool KMBuilderGlobal::cpUID(string srcPath, string descPath)
{
    fs::path fpath(srcPath);

    struct stat st;
    if (stat(fpath.c_str(), &st) == 0)
    {
        // 成功获取文件信息
        std::cout << "UID: " << st.st_uid << std::endl;
        std::cout << "GID: " << st.st_gid << std::endl;
    }
    else
    {
        // 错误处理
        perror("stat");
        return false;
    }

    // 调用 chown 函数
    if (chown(descPath.c_str(), st.st_uid, st.st_gid) == -1)
    {
        // chown 调用失败
        std::cerr << "chown failed: " << strerror(errno) << std::endl;
        return 1;
    }

    // chown 调用成功
    std::cout << "File ownership changed successfully." << std::endl;
    return true;
}

bool KMBuilderGlobal::cpMod(string srcPath, string descPath)
{
    struct stat st;
    // 获取第一个文件的权限
    if (stat(srcPath.c_str(), &st) == -1)
    {
        std::cerr << "stat failed for " << srcPath << ": " << strerror(errno) << std::endl;
        return false;
    }

    // 从 stat 结构体中提取权限（通常是 st_mode 成员）
    mode_t mode = st.st_mode;

    // 设置第二个文件的权限
    if (chmod(descPath.c_str(), mode) == -1)
    {
        std::cerr << "chmod failed for " << descPath << ": " << strerror(errno) << std::endl;
        return false;
    }

    // 权限复制成功
    std::cout << "Permissions copied successfully." << std::endl;

    return true;
}
