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

#pragma once

#include <sys/mount.h>

#include <filesystem>
#include <optional>
#include <string_view>
#include <unordered_map>
#include <vector>

#include <sys/resource.h>
#include <sys/types.h>

#ifdef LINYAPS_BOX_ENABLE_CAP
#  include <sys/capability.h>
#endif

// Compatible with linux kernel which is under 5.10
#ifndef MS_NOSYMFOLLOW
#  define LINGYAPS_MS_NOSYMFOLLOW 256
#else
#  define LINGYAPS_MS_NOSYMFOLLOW MS_NOSYMFOLLOW
#endif

namespace linyaps_box {

struct config
{
    static constexpr auto oci_version = "1.2.1";

    static auto parse(std::string_view content) -> config;

    struct process_t
    {
        bool terminal{ false };

        struct console_size_t
        {
            unsigned short height{ 0 };
            unsigned short width{ 0 };
        };

        std::optional<console_size_t> console_size;

        std::filesystem::path cwd;
        std::vector<std::string> env;
        std::vector<std::string> args;

        struct rlimit_t
        {
            // use string for runtime log
            std::string type;
            uint64_t soft;
            uint64_t hard;
        };

        using rlimits_t = std::vector<rlimit_t>;

        std::optional<rlimits_t> rlimits;
        std::optional<std::string> apparmor_profile;

        // TODO: not use cap_value_t directly
#ifdef LINYAPS_BOX_ENABLE_CAP
        struct capabilities_t
        {
            std::vector<cap_value_t> effective;
            std::vector<cap_value_t> bounding;
            std::vector<cap_value_t> inheritable;
            std::vector<cap_value_t> permitted;
            std::vector<cap_value_t> ambient;
        };

        capabilities_t capabilities;
#endif

        bool no_new_privileges = false;
        std::optional<int> oom_score_adj;

        struct user_t
        {
            uid_t uid;
            gid_t gid;
            std::optional<mode_t> umask;
            std::optional<std::vector<gid_t>> additional_gids;
        };

        // TODO: user is optional
        user_t user;
    };

    process_t process;

    struct linux_t
    {
        struct id_mapping_t
        {
            uid_t host_id;
            uid_t container_id;
            size_t size;
        };

        struct namespace_t
        {
            enum class type : unsigned int {
                NONE = 0,
                IPC = CLONE_NEWIPC,
                UTS = CLONE_NEWUTS,
                MOUNT = CLONE_NEWNS,
                PID = CLONE_NEWPID,
                NET = CLONE_NEWNET,
                USER = CLONE_NEWUSER,
                CGROUP = CLONE_NEWCGROUP,
            };

            type type_{ type::NONE };
            std::optional<std::filesystem::path> path;
        };

        std::optional<std::vector<namespace_t>> namespaces;
        std::optional<std::vector<id_mapping_t>> uid_mappings;
        std::optional<std::vector<id_mapping_t>> gid_mappings;
        std::optional<std::vector<std::filesystem::path>> masked_paths;
        std::optional<std::vector<std::filesystem::path>> readonly_paths;
        unsigned int rootfs_propagation{ 0 };
    };

    std::optional<linux_t> linux;

    struct hooks_t
    {
        struct hook_t
        {
            std::filesystem::path path;
            std::optional<std::vector<std::string>> args;
            std::optional<std::unordered_map<std::string, std::string>> env;
            std::optional<int> timeout;
        };

        std::optional<std::vector<hook_t>> prestart;
        std::optional<std::vector<hook_t>> create_runtime;
        std::optional<std::vector<hook_t>> create_container;
        std::optional<std::vector<hook_t>> start_container;
        std::optional<std::vector<hook_t>> poststart;
        std::optional<std::vector<hook_t>> poststop;
    };

    hooks_t hooks;

    struct mount_t
    {
        enum class extension : std::uint8_t { NONE = 0, COPY_SYMLINK };

        std::optional<std::string> source;
        std::optional<std::filesystem::path> destination;
        std::string type;
        extension extension_flags{ 0 };
        unsigned long flags{ 0 };
        unsigned long propagation_flags{ 0 };
        std::string data;
    };

    std::vector<mount_t> mounts;

    struct root_t
    {
        std::filesystem::path path;
        bool readonly{ false };
    };

    root_t root;

    std::optional<std::unordered_map<std::string, std::string>> annotations;
};

std::string to_string(linyaps_box::config::linux_t::namespace_t::type type) noexcept;

} // namespace linyaps_box

constexpr linyaps_box::config::mount_t::extension
operator|(linyaps_box::config::mount_t::extension lhs, linyaps_box::config::mount_t::extension rhs)
{
    return static_cast<linyaps_box::config::mount_t::extension>(
      static_cast<std::underlying_type_t<linyaps_box::config::mount_t::extension>>(lhs)
      | static_cast<std::underlying_type_t<linyaps_box::config::mount_t::extension>>(rhs));
}

constexpr linyaps_box::config::mount_t::extension
operator&(linyaps_box::config::mount_t::extension lhs, linyaps_box::config::mount_t::extension rhs)
{
    return static_cast<linyaps_box::config::mount_t::extension>(
      static_cast<std::underlying_type_t<linyaps_box::config::mount_t::extension>>(lhs)
      & static_cast<std::underlying_type_t<linyaps_box::config::mount_t::extension>>(rhs));
}

constexpr linyaps_box::config::mount_t::extension &
operator|=(linyaps_box::config::mount_t::extension &lhs,
           linyaps_box::config::mount_t::extension rhs) noexcept
{
    lhs = lhs | rhs;
    return lhs;
}

constexpr linyaps_box::config::mount_t::extension &
operator&=(linyaps_box::config::mount_t::extension &lhs,
           linyaps_box::config::mount_t::extension rhs) noexcept
{
    lhs = lhs & rhs;
    return lhs;
}

constexpr linyaps_box::config::linux_t::namespace_t::type
operator|(linyaps_box::config::linux_t::namespace_t::type lhs,
          linyaps_box::config::linux_t::namespace_t::type rhs) noexcept
{
    return static_cast<linyaps_box::config::linux_t::namespace_t::type>(
      static_cast<std::underlying_type_t<linyaps_box::config::linux_t::namespace_t::type>>(lhs)
      | static_cast<std::underlying_type_t<linyaps_box::config::linux_t::namespace_t::type>>(rhs));
}

constexpr linyaps_box::config::linux_t::namespace_t::type
operator&(linyaps_box::config::linux_t::namespace_t::type lhs,
          linyaps_box::config::linux_t::namespace_t::type rhs) noexcept
{
    return static_cast<linyaps_box::config::linux_t::namespace_t::type>(
      static_cast<std::underlying_type_t<linyaps_box::config::linux_t::namespace_t::type>>(lhs)
      & static_cast<std::underlying_type_t<linyaps_box::config::linux_t::namespace_t::type>>(rhs));
}

constexpr linyaps_box::config::linux_t::namespace_t::type &
operator|=(linyaps_box::config::linux_t::namespace_t::type &lhs,
           linyaps_box::config::linux_t::namespace_t::type rhs) noexcept
{
    lhs = lhs | rhs;
    return lhs;
}

constexpr linyaps_box::config::linux_t::namespace_t::type &
operator&=(linyaps_box::config::linux_t::namespace_t::type &lhs,
           linyaps_box::config::linux_t::namespace_t::type rhs) noexcept
{
    lhs = lhs & rhs;
    return lhs;
}
