/* SPDX-FileCopyrightText: 2025 - 2026 UnionTech Software Technology Co., Ltd.  + *
 * SPDX-License-Identifier: LGPL-3.0-or-later
 */

#include "run_context.h"

#include "linglong/common/display.h"
#include "linglong/common/strings.h"
#include "linglong/extension/extension.h"
#include "linglong/runtime/host_nvidia_extension.h"
#include "linglong/runtime/container_builder.h"
#include "linglong/utils/log/log.h"

#include <fmt/ranges.h>

#include <QDebug>

#include <string_view>
#include <utility>
#include <unordered_set>

namespace linglong::runtime {

namespace {

constexpr std::string_view kNvidiaExtensionPrefix =
  extension::ExtensionImplNVIDIADisplayDriver::Identify;

bool isNvidiaDriverExtensionName(std::string_view name)
{
    return name.rfind(kNvidiaExtensionPrefix, 0) == 0;
}

std::string mergePathValues(const std::string &preferred, const std::string &existing)
{
    std::vector<std::string> ordered;
    std::unordered_set<std::string> seen;
    for (const auto &part : common::strings::split(
           preferred, ':', common::strings::splitOption::SkipEmpty)) {
        auto value = std::string(part);
        if (seen.insert(value).second) {
            ordered.push_back(std::move(value));
        }
    }
    for (const auto &part : common::strings::split(
           existing, ':', common::strings::splitOption::SkipEmpty)) {
        auto value = std::string(part);
        if (seen.insert(value).second) {
            ordered.push_back(std::move(value));
        }
    }
    return common::strings::join(ordered, ':');
}

void mergeEnv(std::map<std::string, std::string> &base,
              const std::map<std::string, std::string> &extra)
{
    static const std::unordered_set<std::string> pathKeys = {
        "LD_LIBRARY_PATH",
        "EGL_EXTERNAL_PLATFORM_CONFIG_DIRS",
        "__EGL_EXTERNAL_PLATFORM_CONFIG_DIRS",
        "__EGL_VENDOR_LIBRARY_DIRS",
        "VK_ICD_FILENAMES",
        "VK_ADD_DRIVER_FILES",
    };

    for (const auto &pair : extra) {
        const auto &key = pair.first;
        const auto &value = pair.second;
        if (value.empty()) {
            continue;
        }
        if (pathKeys.find(key) != pathKeys.end()) {
            auto it = base.find(key);
            std::string merged = mergePathValues(value, it != base.end() ? it->second : "");
            if (!merged.empty()) {
                base[key] = merged;
            }
            continue;
        }
        auto it = base.find(key);
        if (it == base.end() || it->second.empty()) {
            base[key] = value;
        }
    }
}

} // namespace

utils::error::Result<RuntimeLayer> RuntimeLayer::create(package::Reference ref, RunContext &context)
{
    LINGLONG_TRACE(fmt::format("create runtime layer from ref {}", ref.toString()));

    try {
        return RuntimeLayer(ref, context);
    } catch (const std::exception &e) {
        return LINGLONG_ERR("failed to create runtime layer", e);
    }
}

RuntimeLayer::RuntimeLayer(package::Reference ref, RunContext &context)
    : reference(std::move(ref))
    , runContext(context)
    , temporary(false)
{
    const auto &repo = context.getRepo();
    auto item = repo.getLayerItem(reference);
    if (!item) {
        throw std::runtime_error("no cached item found");
    }
    cachedItem = std::move(item).value();
}

RuntimeLayer::~RuntimeLayer()
{
    if (temporary && layerDir) {
        std::error_code ec;
        std::filesystem::remove_all(layerDir->path(), ec);
    }
}

utils::error::Result<void> RuntimeLayer::resolveLayer(const std::vector<std::string> &modules,
                                                      const std::optional<std::string> &subRef)
{
    LINGLONG_TRACE("resolve layer");

    auto &repo = runContext.get().getRepo();
    utils::error::Result<package::LayerDir> layer(LINGLONG_ERR("null"));
    if (modules.empty() || (modules.size() == 1 && modules[0] == "binary")) {
        layer = repo.getMergedModuleDir(reference, true, subRef);
    } else if (modules.size() > 1) {
        layer = repo.createTempMergedModuleDir(reference, modules);
        temporary = true;
    } else {
        return LINGLONG_ERR(
          fmt::format("resolve module {} is not supported", fmt::join(modules, ",")));
    }

    if (!layer) {
        return LINGLONG_ERR("layer doesn't exist: " + reference.toString(), layer);
    }

    layerDir = *layer;
    return LINGLONG_OK;
}

RunContext::~RunContext()
{
    if (!bundle.empty()) {
        std::error_code ec;
        if (std::filesystem::exists(bundle, ec)) {
            if (std::filesystem::remove_all(bundle, ec) == static_cast<std::uintmax_t>(-1)) {
                LogW("failed to remove bundle directory {}: {}", bundle, ec.message());
            }
        }
    }
}

utils::error::Result<void> RunContext::resolve(const linglong::package::Reference &runnable,
                                               const ResolveOptions &options)
{
    LINGLONG_TRACE("resolve RunContext from runnable " + runnable.toString());
    hostNvidiaExtensionName.reset();

    auto layer = RuntimeLayer::create(runnable, *this);
    if (!layer) {
        return LINGLONG_ERR(layer);
    }

    containerID = runtime::genContainerID(runnable);

    const auto &info = layer->getCachedItem().info;
    if (info.kind == "base") {
        baseLayer = std::move(layer).value();
    } else if (info.kind == "app") {
        appLayer = std::move(layer).value();
        auto runtime = options.runtimeRef.value_or(info.runtime.value_or(""));
        if (!runtime.empty()) {
            auto runtimeFuzzyRef = package::FuzzyReference::parse(runtime);
            if (!runtimeFuzzyRef) {
                return LINGLONG_ERR(runtimeFuzzyRef);
            }

            auto ref = repo.clearReference(*runtimeFuzzyRef,
                                           {
                                             .forceRemote = false,
                                             .fallbackToRemote = false,
                                             .semanticMatching = true,
                                           });
            if (!ref) {
                return LINGLONG_ERR("ref doesn't exist " + runtimeFuzzyRef->toString());
            }
            auto res = RuntimeLayer::create(std::move(ref).value(), *this);
            if (!res) {
                return LINGLONG_ERR(res);
            }
            runtimeLayer = std::move(res).value();
        }
    } else if (info.kind == "runtime") {
        runtimeLayer = std::move(layer).value();
    } else {
        return LINGLONG_ERR(fmt::format("kind {} is not runnable", info.kind));
    }

    // base layer must be resolved for all kinds
    if (!baseLayer) {
        auto baseRef = options.baseRef.value_or(info.base);
        auto baseFuzzyRef = package::FuzzyReference::parse(baseRef);
        if (!baseFuzzyRef) {
            return LINGLONG_ERR(baseFuzzyRef);
        }

        auto ref = repo.clearReference(*baseFuzzyRef,
                                       {
                                         .forceRemote = false,
                                         .fallbackToRemote = false,
                                         .semanticMatching = true,
                                       });
        if (!ref) {
            return LINGLONG_ERR(ref);
        }
        auto res = RuntimeLayer::create(std::move(ref).value(), *this);
        if (!res) {
            return LINGLONG_ERR(res);
        }
        baseLayer = std::move(res).value();
    }

    // resolve base extension
    auto ret = resolveExtension(
      *baseLayer,
      matchedExtensionDefines(baseLayer->getReference(), options.externalExtensionDefs));
    if (!ret) {
        return LINGLONG_ERR(ret);
    }

    // resolve runtime extension
    if (runtimeLayer) {
        auto ret = resolveExtension(
          *runtimeLayer,
          matchedExtensionDefines(runtimeLayer->getReference(), options.externalExtensionDefs));
        if (!ret) {
            return LINGLONG_ERR(ret);
        }
    }

    // resolve app extension
    if (appLayer) {
        auto ret = resolveExtension(
          *appLayer,
          matchedExtensionDefines(appLayer->getReference(), options.externalExtensionDefs));
        if (!ret) {
            return LINGLONG_ERR(ret);
        }
    }

    // 手动解析多个扩展
    if (options.extensionRefs && !options.extensionRefs->empty()) {
        auto manualExtensionDef = makeManualExtensionDefine(*options.extensionRefs);
        if (!manualExtensionDef) {
            return LINGLONG_ERR(manualExtensionDef);
        }

        auto ret = resolveExtension(*manualExtensionDef);
        if (!ret) {
            return LINGLONG_ERR(ret);
        }
    }

    // all reference are cleard , we can get actual layer directory now
    return resolveLayer(options.depsBinaryOnly,
                        options.appModules.value_or(std::vector<std::string>{}));
}

utils::error::Result<void> RunContext::resolve(const api::types::v1::BuilderProject &target,
                                               const std::filesystem::path &buildOutput)
{
    LINGLONG_TRACE("resolve RunContext from builder project " + target.package.id);
    hostNvidiaExtensionName.reset();

    auto targetRef = package::Reference::fromBuilderProject(target);
    if (!targetRef) {
        return LINGLONG_ERR(targetRef);
    }
    containerID = runtime::genContainerID(*targetRef);
    targetId = target.package.id;

    if (target.package.kind == "extension") {
        extensionOutput = buildOutput;
    } else if (target.package.kind == "app") {
        appOutput = buildOutput;
    } else if (target.package.kind == "runtime") {
        runtimeOutput = buildOutput;
    } else {
        return LINGLONG_ERR("can't resolve run context from package kind " + target.package.kind);
    }

    auto baseFuzzyRef = package::FuzzyReference::parse(target.base);
    if (!baseFuzzyRef) {
        return LINGLONG_ERR(baseFuzzyRef);
    }

    auto ref = repo.clearReference(*baseFuzzyRef,
                                   {
                                     .forceRemote = false,
                                     .fallbackToRemote = false,
                                     .semanticMatching = true,
                                   });
    if (!ref) {
        return LINGLONG_ERR(ref);
    }
    auto res = RuntimeLayer::create(std::move(ref).value(), *this);
    if (!res) {
        return LINGLONG_ERR(res);
    }
    baseLayer = std::move(res).value();

    if (target.runtime) {
        auto runtimeFuzzyRef = package::FuzzyReference::parse(*target.runtime);
        if (!runtimeFuzzyRef) {
            return LINGLONG_ERR(runtimeFuzzyRef);
        }

        ref = repo.clearReference(*runtimeFuzzyRef,
                                  {
                                    .forceRemote = false,
                                    .fallbackToRemote = false,
                                    .semanticMatching = true,
                                  });
        if (!ref) {
            return LINGLONG_ERR("ref doesn't exist " + runtimeFuzzyRef->toString());
        }
        auto res = RuntimeLayer::create(std::move(ref).value(), *this);
        if (!res) {
            return LINGLONG_ERR(res);
        }
        runtimeLayer = std::move(res).value();

        const auto &info = runtimeLayer->getCachedItem().info;
        auto fuzzyRef = package::FuzzyReference::parse(info.base);
        if (!fuzzyRef) {
            return LINGLONG_ERR(fuzzyRef);
        }
        auto ref = repo.clearReference(*fuzzyRef,
                                       {
                                         .forceRemote = false,
                                         .fallbackToRemote = false,
                                         .semanticMatching = true,
                                       });
        if (!ref || *ref != baseLayer->getReference()) {
            auto msg = fmt::format("Base is not compatible with runtime. \n - Current base: {}\n - "
                                   "Current runtime: {}\n - Base required by runtime: {}",
                                   baseLayer->getReference().toString(),
                                   runtimeLayer->getReference().toString(),
                                   info.base);
            return LINGLONG_ERR(msg);
        }
    }

    return resolveLayer(false, {});
}

utils::error::Result<void> RunContext::resolveLayer(bool depsBinaryOnly,
                                                    const std::vector<std::string> &appModules)
{
    LINGLONG_TRACE("resolve layers");

    std::optional<std::string> subRef;
    if (appLayer) {
        const auto &info = appLayer->getCachedItem().info;
        if (info.uuid) {
            subRef = info.uuid;
        }
    }

    std::vector<std::string> depsModules;
    if (depsBinaryOnly) {
        depsModules.emplace_back("binary");
    }
    auto ref = baseLayer->resolveLayer(depsModules, subRef);
    if (!ref.has_value()) {
        return LINGLONG_ERR("failed to resolve base layer", ref);
    }

    if (appLayer) {
        auto ref = appLayer->resolveLayer(appModules);
        if (!ref.has_value()) {
            return LINGLONG_ERR("failed to resolve app layer", ref);
        }
    }

    if (runtimeLayer) {
        auto ref = runtimeLayer->resolveLayer(depsModules, subRef);
        if (!ref.has_value()) {
            return LINGLONG_ERR("failed to resolve runtime layer", ref);
        }
    }

    for (auto &ext : extensionLayers) {
        if (!ext.resolveLayer()) {
            LogW("ignore failed extension layer");
            continue;
        }

        auto extensionOf = ext.getExtensionInfo();
        if (!extensionOf) {
            continue;
        }

        const auto &[extensionDefine, layer] = *extensionOf;
        const auto &extInfo = ext.getCachedItem().info;
        if (!extInfo.extImpl) {
            LogW("no ext_impl found for {}", ext.getReference().toString());
            continue;
        }
        const auto &extImpl = *extInfo.extImpl;
        if (!extImpl.env) {
            continue;
        }
        for (const auto &env : *extImpl.env) {
            // if allowEnv is not defined, all envs are allowed
            std::string defaultValue;
            if (extensionDefine.allowEnv) {
                const auto &allowEnv = *extensionDefine.allowEnv;
                auto allowed = allowEnv.find(env.first);
                if (allowed == allowEnv.end()) {
                    LogW("env {} not allowed in {}", env.first, ext.getReference().toString());
                    continue;
                }
                defaultValue = allowed->second;
            }

            std::string res =
              common::strings::replaceSubstring(env.second,
                                                "$PREFIX",
                                                "/opt/extensions/" + ext.getReference().id);
            auto &value = environment[env.first];
            if (value.empty()) {
                value = defaultValue;
            }
            // If $ORIGIN is unset and the default value is empty, the environment variable
            // may become ":NEW_VALUE" or "NEW_VALUE:". We cannot remove the leading/trailing
            // colon because the value might represent a non-path element (e.g., a delimiter)
            res = common::strings::replaceSubstring(res, "$ORIGIN", value);

            value = res;
            LogD("environment[{}]={}", env.first, res);
        }
    }

    return LINGLONG_OK;
}

utils::error::Result<void> RunContext::resolveExtension(
  RuntimeLayer &layer, const std::vector<api::types::v1::ExtensionDefine> &externalExtensionDefs)
{
    LINGLONG_TRACE("resolve RuntimeLayer extension");

    const auto &info = layer.getCachedItem().info;
    if (info.extensions) {
        auto res = resolveExtension(*info.extensions, info.channel, true);
        if (!res) {
            return LINGLONG_ERR(res);
        }
    }

    // merge external extensions
    if (!externalExtensionDefs.empty()) {
        return resolveExtension(externalExtensionDefs, info.channel, true);
    }

    return LINGLONG_OK;
}

utils::error::Result<void>
RunContext::resolveExtension(const std::vector<api::types::v1::ExtensionDefine> &extDefs,
                             std::optional<std::string> channel,
                             bool skipOnNotFound)
{
    LINGLONG_TRACE("resolve extension define");

    for (const auto &extDef : extDefs) {
        LogD("handle extensions: {}", extDef.name);
        LogD("version: {}", extDef.version);
        LogD("directory: {}", extDef.directory);
        if (extDef.allowEnv) {
            for (const auto &allowEnv : *extDef.allowEnv) {
                LogD("allowEnv: {}:{}", allowEnv.first, allowEnv.second);
            }
        }

        std::string name = extDef.name;
        auto ext = extension::ExtensionFactory::makeExtension(name);
        if (!ext->shouldEnable(name)) {
            continue;
        }

        std::optional<std::string> version;
        if (!extDef.version.empty()) {
            version = extDef.version;
        }
        auto fuzzyRef = package::FuzzyReference::create(channel, name, version, std::nullopt);
        auto ref =
          repo.clearReference(*fuzzyRef, { .fallbackToRemote = false, .semanticMatching = true });
        if (!ref) {
            LogD("extension is not installed: {}", fuzzyRef->toString());
            if (isNvidiaDriverExtensionName(name)) {
                if (!hostNvidiaExtensionName) {
                    hostNvidiaExtensionName = name;
                    LogI("use host NVIDIA driver fallback for {}", name);
                }
                continue;
            }
            if (skipOnNotFound) {
                continue;
            }
            return LINGLONG_ERR("extension is not installed", ref);
        }

        auto layer = RuntimeLayer::create(*ref, *this);
        if (!layer) {
            return LINGLONG_ERR(layer);
        }

        if (layer->getCachedItem().info.kind != "extension") {
            return LINGLONG_ERR(fmt::format("{} is not an extension", ref->toString()));
        }

        auto &extensionLayer = extensionLayers.emplace_back(std::move(layer).value());
        extensionLayer.setExtensionInfo(
          std::make_pair(extDef, std::reference_wrapper<RuntimeLayer>(extensionLayer)));
    }

    return LINGLONG_OK;
}

utils::error::Result<std::vector<api::types::v1::ExtensionDefine>>
RunContext::makeManualExtensionDefine(const std::vector<std::string> &refs)
{
    LINGLONG_TRACE("make extension define");

    std::vector<api::types::v1::ExtensionDefine> extDefs;
    extDefs.reserve(refs.size());
    for (const auto &ref : refs) {
        auto fuzzyRef = package::FuzzyReference::parse(ref);
        if (!fuzzyRef) {
            return LINGLONG_ERR("failed to parse extension ref", fuzzyRef);
        }

        extDefs.emplace_back(api::types::v1::ExtensionDefine{
          .directory = "/opt/extensions/" + fuzzyRef->id,
          .name = fuzzyRef->id,
          .version = fuzzyRef->version.value_or(""),
        });
    }
    return extDefs;
}

std::vector<api::types::v1::ExtensionDefine> RunContext::matchedExtensionDefines(
  const package::Reference &ref,
  const std::optional<std::map<std::string, std::vector<api::types::v1::ExtensionDefine>>>
    &externalExtensionDefs)
{
    std::vector<api::types::v1::ExtensionDefine> result;

    if (externalExtensionDefs.has_value()) {
        for (const auto &[key, defs] : *externalExtensionDefs) {
            auto fuzzyRef = package::FuzzyReference::parse(key);
            if (!fuzzyRef) {
                LogE("invalid ref {}: {}", key, fuzzyRef.error());
                continue;
            }

            if (fuzzyRef->id != ref.id) {
                continue;
            }

            if (fuzzyRef->version) {
                if (!ref.version.semanticMatch(*fuzzyRef->version)) {
                    continue;
                }
            }

            result.insert(result.end(), defs.begin(), defs.end());
        }
    }

    return result;
}

void RunContext::detectDisplaySystem(generator::ContainerCfgBuilder &builder) noexcept
{
    while (true) {
        auto *xOrgAuthFileEnv = ::getenv("XAUTHORITY");
        if (xOrgAuthFileEnv == nullptr || xOrgAuthFileEnv[0] == '\0') {
            LogD("XAUTHORITY is not set, ignore it");
            break;
        }

        auto xOrgAuthFile = common::display::getXOrgAuthFile(xOrgAuthFileEnv);
        if (!xOrgAuthFile) {
            LogW("failed to get XOrg auth file: {}, ignore it", xOrgAuthFile.error());
            break;
        }

        builder.bindXAuthFile(xOrgAuthFile.value());
        break;
    }

    while (true) {
        auto *waylandDisplayEnv = ::getenv("WAYLAND_DISPLAY");
        if (waylandDisplayEnv == nullptr || waylandDisplayEnv[0] == '\0') {
            LogD("WAYLAND_DISPLAY is not set, ignore it");
            break;
        }

        auto waylandDisplay = common::display::getWaylandDisplay(waylandDisplayEnv);
        if (!waylandDisplay) {
            LogW("failed to get Wayland display: {}, ignore it", waylandDisplay.error());
            break;
        }

        builder.bindWaylandSocket(waylandDisplay.value());
        break;
    }
}

utils::error::Result<void> RunContext::fillContextCfg(
  linglong::generator::ContainerCfgBuilder &builder, const std::string &bundleSuffix)
{
    LINGLONG_TRACE("fill ContainerCfgBuilder with run context");

    builder.setContainerId(containerID);

    if (!baseLayer) {
        return LINGLONG_ERR("run context doesn't resolved");
    }

    auto bundleDir = runtime::makeBundleDir(containerID, bundleSuffix);
    if (!bundleDir) {
        return LINGLONG_ERR("failed to get bundle dir of " + containerID);
    }
    bundle = *bundleDir;
    builder.setBundlePath(bundle);

    builder.setBasePath(baseLayer->getLayerDir()->filesDirPath());

    if (appOutput) {
        builder.setAppPath(*appOutput, false);
    } else {
        if (appLayer) {
            builder.setAppPath(appLayer->getLayerDir()->filesDirPath());
        }
    }

    if (runtimeOutput) {
        builder.setRuntimePath(*runtimeOutput, false);
    } else {
        if (runtimeLayer) {
            builder.setRuntimePath(runtimeLayer->getLayerDir()->filesDirPath());
        }
    }

    std::vector<ocppi::runtime::config::types::Mount> extensionMounts{};
    std::optional<HostNvidiaExtension> hostNvidiaExtension;
    if (extensionOutput) {
        extensionMounts.push_back(ocppi::runtime::config::types::Mount{
          .destination = "/opt/extensions/" + targetId,
          .gidMappings = {},
          .options = { { "rbind" } },
          .source = extensionOutput,
          .type = "bind",
          .uidMappings = {},
        });
    }

    for (auto &ext : extensionLayers) {
        const auto &info = ext.getCachedItem().info;
        if (info.extImpl && info.extImpl->deviceNodes) {
            for (auto &node : *info.extImpl->deviceNodes) {
                ocppi::runtime::config::types::Mount mount = {
                    .destination = node.path,
                    .options = { { "bind" } },
                    .source = node.hostPath.value_or(node.path),
                    .type = "bind",
                };
                builder.addExtraMount(mount);
            }
        }

        std::string name = ext.getReference().id;
        if (extensionOutput && name == targetId) {
            continue;
        }
        extensionMounts.push_back(ocppi::runtime::config::types::Mount{
          .destination = "/opt/extensions/" + name,
          .gidMappings = {},
          .options = { { "rbind", "ro" } },
          .source = ext.getLayerDir()->filesDirPath(),
          .type = "bind",
          .uidMappings = {},
        });
    }

    if (hostNvidiaExtensionName) {
        auto hostExt = prepareHostNvidiaExtension(bundle, *hostNvidiaExtensionName);
        if (!hostExt) {
            return LINGLONG_ERR(hostExt);
        }
        if (hostExt->has_value()) {
            hostNvidiaExtension = std::move(*hostExt);
            bool mountHostExtension =
              !(extensionOutput && hostNvidiaExtension->name == targetId);
            if (mountHostExtension) {
                extensionMounts.push_back(ocppi::runtime::config::types::Mount{
                  .destination = "/opt/extensions/" + hostNvidiaExtension->name,
                  .gidMappings = {},
                  .options = { { "rbind", "ro" } },
                  .source = hostNvidiaExtension->root.string(),
                  .type = "bind",
                  .uidMappings = {},
                });
                for (const auto &node : hostNvidiaExtension->deviceNodes) {
                    ocppi::runtime::config::types::Mount mount = {
                        .destination = node.path,
                        .options = { { "bind" } },
                        .source = node.hostPath.value_or(node.path),
                        .type = "bind",
                    };
                    builder.addExtraMount(mount);
                }
                if (!hostNvidiaExtension->extraMounts.empty()) {
                    builder.addExtraMounts(hostNvidiaExtension->extraMounts);
                }
                mergeEnv(environment, hostNvidiaExtension->env);
            }
        }
    }
    if (!extensionMounts.empty()) {
        builder.setExtensionMounts(extensionMounts);
    }

    auto res = fillExtraAppMounts(builder);
    if (!res) {
        return res;
    }

    if (!environment.empty()) {
        builder.appendEnv(environment);
    }

    detectDisplaySystem(builder);

    for (auto ctx = securityContexts.begin(); ctx != securityContexts.end(); ++ctx) {
        auto manager = getSecurityContextManager(ctx->first);
        if (!manager) {
            auto msg = "failed to get security context manager: " + fromType(ctx->first);
            return LINGLONG_ERR(msg.c_str());
        }

        auto secCtx = manager->createSecurityContext(builder);
        if (!secCtx) {
            auto msg = "failed to create security context: " + fromType(ctx->first);
            return LINGLONG_ERR(msg.c_str());
        }
        ctx->second = std::move(secCtx).value();

        auto res = ctx->second->apply(builder);
        if (!res) {
            auto msg = "failed to apply security context: " + fromType(ctx->first);
            ctx = securityContexts.erase(ctx);
            return LINGLONG_ERR(msg.c_str(), res);
        }
    }

    return LINGLONG_OK;
}

void RunContext::enableSecurityContext(const std::vector<SecurityContextType> &ctxs)
{
    for (const auto &type : ctxs) {
        securityContexts.try_emplace(type, nullptr);
    }
}

utils::error::Result<void> RunContext::fillExtraAppMounts(generator::ContainerCfgBuilder &builder)
{
    LINGLONG_TRACE("fill extra app mounts");

    auto fillPermissionsBinds = [&builder,
                                 this](RuntimeLayer &layer) -> utils::error::Result<void> {
        LINGLONG_TRACE("fill permissions binds");

        const auto &info = layer.getCachedItem().info;

        if (info.permissions) {
            std::vector<ocppi::runtime::config::types::Mount> applicationMounts{};
            auto bindMount =
              [&applicationMounts](
                const api::types::v1::ApplicationConfigurationPermissionsBind &bind) {
                  applicationMounts.push_back(ocppi::runtime::config::types::Mount{
                    .destination = bind.destination,
                    .gidMappings = {},
                    .options = { { "rbind" } },
                    .source = bind.source,
                    .type = "bind",
                    .uidMappings = {},
                  });
              };

            auto bindInnerMount =
              [&applicationMounts,
               this](const api::types::v1::ApplicationConfigurationPermissionsInnerBind &bind) {
                  applicationMounts.push_back(ocppi::runtime::config::types::Mount{
                    .destination = bind.destination,
                    .gidMappings = {},
                    .options = { { "rbind" } },
                    .source = bundle.string() + "/rootfs" + bind.source,
                    .type = "bind",
                    .uidMappings = {},
                  });
              };

            const auto &perm = info.permissions;
            if (perm->binds) {
                const auto &binds = perm->binds;
                std::for_each(binds->cbegin(), binds->cend(), bindMount);
            }

            if (perm->innerBinds) {
                const auto &innerBinds = perm->innerBinds;
                std::for_each(innerBinds->cbegin(), innerBinds->cend(), bindInnerMount);
            }

            builder.addExtraMounts(applicationMounts);
        }

        return LINGLONG_OK;
    };

    if (appLayer) {
        auto res = fillPermissionsBinds(*appLayer);
        if (!res) {
            return LINGLONG_ERR("failed to apply permission binds for "
                                  + appLayer->getReference().toString(),
                                res);
        }
    }

    for (auto &ext : extensionLayers) {
        if (!fillPermissionsBinds(ext)) {
            LogW("failed to apply permission binds for {}", ext.getReference().toString());
            continue;
        }
    }

    return LINGLONG_OK;
}

api::types::v1::ContainerProcessStateInfo RunContext::stateInfo()
{
    auto state = linglong::api::types::v1::ContainerProcessStateInfo{
        .containerID = containerID,
    };

    if (baseLayer) {
        state.base = baseLayer->getReference().toString();
    }

    if (appLayer) {
        state.app = appLayer->getReference().toString();
    }

    if (runtimeLayer) {
        state.runtime = runtimeLayer->getReference().toString();
    }

    state.extensions = std::vector<std::string>{};
    for (auto &ext : extensionLayers) {
        state.extensions->push_back(ext.getReference().toString());
    }

    return state;
}

utils::error::Result<std::filesystem::path> RunContext::getBaseLayerPath() const
{
    LINGLONG_TRACE("get base layer path");

    if (!baseLayer) {
        return LINGLONG_ERR("run context doesn't resolved");
    }

    return baseLayer->getLayerDir()->path();
}

utils::error::Result<std::filesystem::path> RunContext::getRuntimeLayerPath() const
{
    LINGLONG_TRACE("get runtime layer path");

    if (!runtimeLayer) {
        return LINGLONG_ERR("no runtime layer exist");
    }

    return runtimeLayer->getLayerDir()->path();
}

utils::error::Result<api::types::v1::RepositoryCacheLayersItem> RunContext::getCachedAppItem()
{
    LINGLONG_TRACE("get cached app item");

    if (!appLayer) {
        return LINGLONG_ERR("no app layer exist");
    }

    return appLayer->getCachedItem();
}

} // namespace linglong::runtime
