#include <buildboxcommon_runnerutils.h>
#include <buildboxcommon_stringutils.h>

static const std::string CHROOT_ROOT_PROP_NAME = "chrootRootDigest";
static const std::string CHROOT_TREE_PROP_NAME = "chrootTreeDigest";
static const std::string CONTAINER_IMAGE_PROP_NAME = "container-image";

namespace buildboxcommon {
namespace buildboxrun {

std::optional<Digest> hasChrootRootDigest(const Platform &platform)
{
    for (const auto &property : platform.properties()) {
        if (property.name() == CHROOT_ROOT_PROP_NAME) {
            try {
                auto chrootRootDigest =
                    StringUtils::digest_from_string(property.value());
                return std::optional<Digest>{chrootRootDigest};
            }
            catch (const std::exception &exception) {
                BUILDBOXCOMMON_THROW_EXCEPTION(
                    std::runtime_error, "Found invalid '"
                                            << CHROOT_ROOT_PROP_NAME
                                            << "' with: " << exception.what());
            }
        }
    }
    return std::nullopt;
}

std::optional<Digest> hasChrootTreeDigest(const Platform &platform)
{
    for (const auto &property : platform.properties()) {
        if (property.name() == CHROOT_TREE_PROP_NAME) {
            try {
                auto chrootTreeDigest =
                    StringUtils::digest_from_string(property.value());
                return std::optional<Digest>{chrootTreeDigest};
            }
            catch (const std::exception &exception) {
                BUILDBOXCOMMON_THROW_EXCEPTION(
                    std::runtime_error, "Found invalid '"
                                            << CHROOT_TREE_PROP_NAME
                                            << "' with: " << exception.what());
            }
        }
    }
    return std::nullopt;
}

std::optional<std::string> hasContainerImage(const Platform &platform)
{
    for (const auto &property : platform.properties()) {
        if (property.name() == CONTAINER_IMAGE_PROP_NAME) {
            return std::optional<std::string>{property.value()};
        }
    }
    return std::nullopt;
}

Digest mergeTrees(const DirectoryTree &inputTree,
                  const DirectoryTree &chrootTree,
                  digest_string_map *mergedDirectoryBlobs)
{
    Digest mergedRootDigest;

    // Using a layered merge where the first layer should be the chrootTree
    // and second layer should be the input tree. In case of conflicts the
    // inputTree should be preferred

    const std::vector<DirectoryTree> treesToMerge = {chrootTree, inputTree};
    const bool success = MergeUtil::createMergedLayersDigest(
        treesToMerge, &mergedRootDigest, mergedDirectoryBlobs);
    if (success) {
        return mergedRootDigest;
    }
    else {
        return {};
    }
}

} // namespace buildboxrun
} // namespace buildboxcommon