#include "KMBuilderPlatform.h"

KMBuilderPlatform::KMBuilderPlatform(shared_ptr<KMBuilderCache> cache)
{
    m_pConfigData = builderContext.getBuilderConfig()->getConfigData();
    m_pKMBuilderCache = cache;
    m_builderCache = m_pKMBuilderCache->getBuilderCache();
}

KMBuilderPlatform::~KMBuilderPlatform()
{
}

bool KMBuilderPlatform::configCreatePlatform()
{
    //判断指令是否执行CreatePlatform
    if (builderContext.isBuildOnly() || builderContext.isExportOnly())
    {
        return false;
    }

    string configIdPlatform = std::any_cast<string>(m_pConfigData->attributeMap["id-platform"]);
    bool configBuildRuntime = std::any_cast<bool>(m_pConfigData->attributeMap["build-runtime"]);

    if (!configBuildRuntime || configIdPlatform.empty())
    {
        return true;
    }

    if (!configCreatePlatformBase())
        return false;

    if (!configPreparePlatform())
        return false;

    if (!configFinishPlatform())
        return false;

    return true;
}

bool KMBuilderPlatform::configCreatePlatformBase()
{
    string configId = std::any_cast<string>(m_pConfigData->attributeMap["id"]);
    string configRuntime = std::any_cast<string>(m_pConfigData->attributeMap["runtime"]);
    string configRuntimeVersion = std::any_cast<string>(m_pConfigData->attributeMap["runtime-version"]);
    string configMetadataPlatform = std::any_cast<string>(m_pConfigData->attributeMap["metadata-platform"]);
    string configIdPlatform = std::any_cast<string>(m_pConfigData->attributeMap["id-platform"]);

    bool configSeparateLocales = std::any_cast<bool>(m_pConfigData->attributeMap["separate-locales"]);

    configChecksumForPlatformBase();
    if (!m_pKMBuilderCache->find("platform-base"))
    {
        KMInfo("Creating platform based on " + configRuntime);
        KMInfo("\033]2;kaiming-builder: Creating platform base for " + configId);

        string appDir = m_builderCache.appDir;
        string platformDir = KMBuilderUtils::getChildPath(appDir, "platform");
        vector<string> args;

        args.push_back("kaiming");
        args.push_back("build-init");
        args.push_back("--update");
        args.push_back("--writable-sdk");
        args.push_back("--sdk-dir=platform");
        args.push_back(KMBuilderUtils::argSplice("--arch=", builderContext.getArch()));

        vector<string> platformExtensions = m_pConfigData->platformExtensions;
        for (auto platformExtension : platformExtensions)
        {
            args.push_back(KMBuilderUtils::argSplice("--sdk-extension=", platformExtension));
        }

        args.push_back(appDir);
        args.push_back(configId);
        args.push_back(configRuntime);
        args.push_back(configRuntime);
        args.push_back(configRuntimeVersion.empty() ? DEFAULT_BRANCH : configRuntimeVersion);

        if (!KMBuilderUtils::spawn("", G_SUBPROCESS_FLAGS_NONE, args))
        {
            return false;
        }

        if (configSeparateLocales)
        {
            if (!KMBuilderUtils::migrateLocaleDirs(platformDir))
            {
                return false;
            }
        }

        if (!configMetadataPlatform.empty())
        {
            string baseDir = builderContext.getBaseDir();
            string destMetadata = KMBuilderUtils::getChildPath(appDir, "metadata.platform");
            string srcMetadata = baseDir + "/" + configMetadataPlatform;
            string contents = "";
            gsize length;

            contents = KMBuilderUtils::fileGetContents(srcMetadata);
            if (contents.empty())
            {
                return false;
            }

            if (!KMBuilderUtils::fileSetContents(destMetadata, contents))
            {
                return false;
            }
        }
        else
        {
            string metadata = KMBuilderUtils::getChildPath(appDir, "metadata");
            string destMetadata = KMBuilderUtils::getChildPath(appDir, "metadata.platform");
            //处理INI参数
            pt::ptree ptKeyFile;
            if (!KMBuilderUtils::loadIniFile(metadata, ptKeyFile))
            {
                KMError("Can't load metadata file: " + metadata);
                return false;
            }

            KMBuilderUtils::setValue(ptKeyFile, "Runtime", "name", configIdPlatform);

            vector<string> groups = KMBuilderUtils::getAllGroupNames(ptKeyFile);
            for (auto group : groups)
            {
                string ext;

                string strExtension = "Extension ";
                if (!group.find(strExtension) == 0)
                {
                    continue;
                }
                //去除Extension头
                ext = group.substr(strExtension.size());

                vector<string> configInheritSdkExtensions = m_pConfigData->inheritSdkExtensions;
                bool bIse = false;
                auto it = std::find(configInheritSdkExtensions.begin(), configInheritSdkExtensions.end(), ext);
                if (it != configInheritSdkExtensions.end())
                {
                    bIse = true;
                }
                if (std::string::npos != ext.find(configId) || (!configInheritSdkExtensions.empty() && bIse))
                {
                    KMBuilderUtils::removeGroup(ptKeyFile, group);
                }
            }

            if (!KMBuilderUtils::saveToIniFile(ptKeyFile, destMetadata))
            {
                KMError("Can't save metadata.platform: " + destMetadata);
                return false;
            }
        }

        //提交commit！
        if (!m_pKMBuilderCache->commit("Created platform base"))
        {
            return false;
        }
    }
    else
    {
        KMInfo("Cache hit for create platform base, skipping");
    }

    return true;
}

bool KMBuilderPlatform::configPreparePlatform()
{
    KMBuilderGlobal global;
    string configId = std::any_cast<string>(m_pConfigData->attributeMap["id"]);
    bool configSeparateLocales = std::any_cast<bool>(m_pConfigData->attributeMap["separate-locales"]);
    vector<string> configPreparePlatformCommands = m_pConfigData->preparePlatformCommands;

    // 创建一个 std::unordered_map
    std::unordered_map<std::string, bool> toRemoveHT;

    configChecksumForPlatformPrepare();
    if (!m_pKMBuilderCache->find("platform-prepare"))
    {
        vector<string> changes;

        KMInfo("Preparing platform with new changes");
        KMInfo("\033]2;kaiming-builder: Preparing platform for " + configId);

        string appDir = m_builderCache.appDir;
        string platformDir = KMBuilderUtils::getChildPath(appDir, "platform");

        if (!configPreparePlatformCommands.empty())
        {
            vector<string> env = global.builderOptionsGetEnv();
            vector<string> buildArgs = global.builderOptionsGetBuildArgs();
            if (buildArgs.empty())
            {
                return false;
            }

            vector<string> otherArgs;
            otherArgs.push_back("--sdk-dir=platform");
            otherArgs.push_back("--metadata=metadata.platform");

            vector<string> extraArgs = KMBuilderUtils::concatVectors(buildArgs, otherArgs);

            for (auto configPreparePlatformCommand : configPreparePlatformCommands)
            {
                if (!KMBuilderUtils::command(appDir, env, extraArgs, configPreparePlatformCommand))
                {
                    return false;
                }
            }
        }

        shared_ptr<KMBuilderConfig> pBuilderConfig = builderContext.getBuilderConfig();
        vector<shared_ptr<KMBuilderModule>> vBuilderModule = pBuilderConfig->getModules();
        for (auto m_pBuilderModule : vBuilderModule)
        {
            global.builderModuleCleanupCollect(m_pBuilderModule, true, toRemoveHT);
        }

        /* This returns both additions and removals */
        changes = m_pKMBuilderCache->cacheGetAllChanges();
        if (changes.empty())
        {
            return false;
        }

        for (auto change : changes)
        {
            string src = "";
            string dest = "";
            string info = src;

            if (!KMBuilderUtils::startsWith(change, "usr/"))
            {
                continue;
            }

            if (KMBuilderUtils::startsWith(change, "usr/lib/debug/") && !KMBuilderUtils::startsWith(change, "usr/lib/debug/app"))
            {
                continue;
            }

            src = appDir + "/" + change;
            dest = platformDir + "/" + change.substr(strlen("usr/"));

            if (info.empty())
            {
                if (!fs::remove(dest))
                {
                    return false;
                }
                continue;
            }

            if (KMBuilderUtils::ifContains(toRemoveHT, change))
            {
                KMInfo("Ignoring " + change);
                continue;
            }

            if (fs::is_directory(info))
            {
                try
                {
                    fs::create_directories(dest);
                    KMInfo("Directories created successfully.");
                }
                catch (const fs::filesystem_error &e)
                {
                    KMError(std::string("Failed to create directories: ") + e.what());
                    return false;
                }
            }
            else
            {
                string destParent = fs::path(dest).parent_path().string();
                try
                {
                    fs::create_directories(destParent);
                    KMInfo("Directories created successfully.");
                }
                catch (const fs::filesystem_error &e)
                {
                    KMError(std::string("Failed to create directories: ") + e.what());
                    return false;
                }

                if (!fs::remove(dest))
                {
                    return false;
                }

                if (KMBuilderUtils::isStringSymlink(info))
                {
                    string symlinkTarget = KMBuilderUtils::getSymlinkTarget(info);
                    int result = symlink(dest.c_str(), symlinkTarget.c_str());
                    if (result == 0)
                    {
                        KMInfo("Symlink created successfully.");
                    }
                    else
                    {
                        KMError(std::string("Failed to create symlink: ") + strerror(errno));
                        return false;
                    }
                }
                else
                {
                    string srcPath = src;
                    string destPath = dest;
                    int result = symlink(srcPath.c_str(), destPath.c_str());
                    if (result == 0)
                    {
                        KMInfo("Symlink created successfully.");
                    }
                    else
                    {
                        KMError(std::string("Failed to create symlink: ") + strerror(errno));
                        return false;
                    }
                }
            }
        }

        //提交commit！
        if (!m_pKMBuilderCache->commit("Prepared platform"))
        {
            return false;
        }
    }
    else
    {
        KMInfo("Cache hit for prepare platform, skipping");
    }

    return true;
}

bool KMBuilderPlatform::configFinishPlatform()
{
    //用于获取全局数据
    KMBuilderGlobal global;

    bool buildRuntime = std::any_cast<bool>(m_pConfigData->attributeMap["build-runtime"]);
    bool buildExtension = std::any_cast<bool>(m_pConfigData->attributeMap["build_extension"]);
    string idPlatform = std::any_cast<string>(m_pConfigData->attributeMap["id-platform"]);
    string branch = std::any_cast<string>(m_pConfigData->attributeMap["branch"]);
    string configSeparateLocales = std::any_cast<string>(m_pConfigData->attributeMap["separate-locales"]);
    string id = std::any_cast<string>(m_pConfigData->attributeMap["id"]);

    configChecksumForPlatformFinish();
    if (!m_pKMBuilderCache->find("platform-finish"))
    {
        string appDir = "";
        string platformDir = "";
        string localeDir = "";
        string ref = "";
        vector<string> subIds;
        int i;

        KMInfo("Finishing platform");
        KMInfo("\033]2;kaiming-builder: Finishing up platform for  " + id);

        appDir = m_builderCache.appDir;
        platformDir = KMBuilderUtils::getChildPath(appDir, "platform");
        localeDir = platformDir + "/" + SEPARATE_DIR;

        //获取ref

        ref = global.kaimingComposeRef(!buildRuntime && !buildExtension, idPlatform, branch, builderContext.getArch());

        vector<string> configCleanupPlatformCommands = m_pConfigData->cleanupPlatformCommands;
        if (!configCleanupPlatformCommands.empty())
        {
            vector<string> env = global.builderOptionsGetEnv();
            vector<string> buildArgs = global.builderOptionsGetBuildArgs();
            if (buildArgs.empty())
            {
                return false;
            }

            vector<string> otherArgs;
            otherArgs.push_back("--sdk-dir=platform");
            otherArgs.push_back("--metadata=metadata.platform");

            vector<string> extraArgs = KMBuilderUtils::concatVectors(buildArgs, otherArgs);

            for (auto configCleanupPlatformCommand : configCleanupPlatformCommands)
            {
                if (!KMBuilderUtils::command(appDir, env, extraArgs, configCleanupPlatformCommand))
                {
                    return false;
                }
            }
        }

        if (!configSeparateLocales.empty() && fs::exists(localeDir))
        {
            string metadataFile = "";
            string extensionContents = "";
            string metadataLocaleFile = "";
            string metadataContents = "";
            string localeId = idPlatform;

            metadataFile = KMBuilderUtils::getChildPath(appDir, "metadata.platform");

            //写入metadata
            std::ostringstream oss;
            oss << "\n"
                << "[Extension " << localeId << "]" << std::endl
                << "directory="
                << "share/runtime/locale" << std::endl
                << "autodelete=true" << std::endl
                << "locale-subset=true" << std::endl;
            extensionContents = oss.str();
            // 清空oss以便复用
            oss.str(""); // 清空缓冲区内容

            fs::path filePath = metadataFile;
            if (!KMBuilderUtils::breakHardlink(filePath))
            {
                KMInfo("Error to breakHardlink " + metadataFile);
                return false;
            }

            if (!KMBuilderUtils::fileSetContents(metadataFile, extensionContents))
            {
                return false;
            }

            metadataLocaleFile = KMBuilderUtils::getChildPath(appDir, "metadata.platform.locale");
            //写入metadata.locale
            metadataLocaleFile = KMBuilderUtils::getChildDir(appDir, "metadata.locale");
            oss << "[Runtime]" << std::endl << "name=" << localeId << std::endl << std::endl << "[ExtensionOf]" << std::endl << "ref=" << ref << std::endl;
            metadataContents = oss.str();
            // 清空oss以便复用
            oss.str(""); // 清空缓冲区内容

            if (!KMBuilderUtils::fileSetContents(metadataLocaleFile, metadataContents))
            {
                return false;
            }
            subIds.push_back(localeId);
        }

        if (subIds.size() > 0)
        {
            string metadataFile = NULL;
            string output = NULL;
            string extensionContents = KMBuilderUtils::argSplice("\n", "[Build]\n", "built-extensions=");

            for (auto subId : subIds)
            {
                extensionContents = KMBuilderUtils::argSplice(extensionContents, subId);
                extensionContents = KMBuilderUtils::argSplice(extensionContents, ";");
            }
            metadataFile = KMBuilderUtils::getChildDir(appDir, "metadata.platform");

            fs::path filePath = metadataFile;
            if (!KMBuilderUtils::breakHardlink(filePath))
            {
                KMInfo("Error to breakHardlink " + metadataFile);
                return false;
            }

            if (!KMBuilderUtils::fileSetContents(metadataFile, extensionContents))
            {
                return false;
            }
        }

        //提交commit！
        if (!m_pKMBuilderCache->commit("Prepared finish"))
        {
            return false;
        }
    }
    else
    {
        KMInfo("Cache hit for platform finish, skipping");
    }

    return true;
}

bool KMBuilderPlatform::configBundleSources()
{
    //用于获取KMBuilderGlobal数据
    KMBuilderGlobal global;
    //获取JSON
    string configPath = builderContext.getConfigFile();
    string json = KMBuilderUtils::fileGetContents(configPath);

    string configId = std::any_cast<string>(m_pConfigData->attributeMap["id"]);

    configChecksumForBundleSources();
    if (!m_pKMBuilderCache->find("bundle-sources"))
    {
        string sourcesId = global.configGetVariousId(".Sources");
        string appDir = "";
        string metadataSourcesFile = "";
        string metadata = "";
        string jsonDir = "";
        string manifestFilename = "";
        string manifestFile = "";
        string metadataContents = "";

        vector<string> subs;
        vector<string> oldSubs;

        km_logger.info("Bundling sources\n");

        appDir = m_builderCache.appDir;
        metadataSourcesFile = KMBuilderUtils::getChildPath(appDir, "metadata.sources");

        //写入metadata
        std::ostringstream oss;
        oss << "[Runtime]" << std::endl << "name=" << sourcesId << std::endl;
        metadataContents = oss.str();
        // 清空oss以便复用
        oss.str(""); // 清空缓冲区内容

        if (!KMBuilderUtils::fileSetContents(metadataSourcesFile, metadataContents))
        {
            return false;
        }

        //生成json配置文件
        jsonDir = appDir + "/" + "sources/manifest";

        try
        {
            // 如果目录不存在，则创建它
            std::filesystem::create_directories(jsonDir);
            std::cout << "Directory created successfully: " << jsonDir << std::endl;
        }
        catch (const std::filesystem::filesystem_error &e)
        {
            // 处理错误
            std::cerr << "Failed to create directory: " << e.what() << std::endl;
            return false;
        }

        manifestFilename = KMBuilderUtils::argSplice(configId, ".json");
        manifestFile = KMBuilderUtils::getChildPath(jsonDir, manifestFilename);
        if (!KMBuilderUtils::fileSetContents(manifestFile, json))
        {
            return false;
        }

        //TUDO:YU bundle_sources功能需要提供
        // for (l = self->expanded_modules; l != NULL; l = l->next)
        // {
        //   KMBuilderModule *m = l->data;

        //   if (!builder_module_bundle_sources (m, context, error))
        //     return FALSE;
        // }

        //添加metadata中的[Build]
        metadata = KMBuilderUtils::getChildPath(appDir, "metadata");

        pt::ptree ptKeyFile;
        if (!KMBuilderUtils::loadIniFile(metadata, ptKeyFile))
        {
            km_logger.error("Can't load metadata file:%s", metadata.c_str());
            return false;
        }

        oldSubs = KMBuilderUtils::getStringListFromPt(ptKeyFile, "Build", "built-extensions");
        for (auto oldSub : oldSubs)
        {
            subs.push_back(oldSub);
        }
        subs.push_back(sourcesId);
        KMBuilderUtils::writeStringListToPt(ptKeyFile, "Build", "built-extensions", subs);

        if (!KMBuilderUtils::saveToIniFile(ptKeyFile, metadata))
        {
            return false;
        }

        //提交commit！
        if (!m_pKMBuilderCache->commit("Bundled sources"))
        {
            return false;
        }
    }
    else
    {
        km_logger.info("Cache hit for bundle-sources, skipping\n");
    }

    return true;
}

void KMBuilderPlatform::configChecksumForPlatformBase()
{
    m_pKMBuilderCache->ChecksumForPlatformBase();
}

void KMBuilderPlatform::configChecksumForPlatformPrepare()
{
    m_pKMBuilderCache->ChecksumForPlatformPrepare();
}

void KMBuilderPlatform::configChecksumForPlatformFinish()
{
    m_pKMBuilderCache->ChecksumForPlatformFinish();
}

void KMBuilderPlatform::configChecksumForBundleSources()
{
    m_pKMBuilderCache->ChecksumForBundleSources();
}

