#ifndef __KMCONFIG_DATA_H__
#define __KMCONFIG_DATA_H__

#include <string>
#include <vector>
#include <map>
#include <optional>
#include <sys/mount.h>
#include <nlohmann/json.hpp>

namespace nlohmann
{
    template<class J, class T>
    inline void from_json(const J &j, std::optional<T> &o)
    {
        if (j.is_null())
        {
            o = std::nullopt;
        }
        else
        {
            o = j.template get<T>();
        }
    }

    template<class J, class T>
    inline void to_json(J &j, const std::optional<T> &o)
    {
        if (o.has_value())
        {
            j = o.value();
        }
    }
} // namespace nlohmann

struct Root
{
    std::string path;
    std::optional<bool> readonly;
};

inline void to_json(nlohmann::json &j, const Root &r)
{
    j["path"] = r.path;
    if (r.readonly)
    {
        j["readonly"] = r.readonly;
    }
}

struct User
{
    std::optional<std::vector<int64_t>> additionalGids;
    std::optional<int64_t> gid;
    std::optional<int64_t> uid;
    std::optional<int64_t> umask;
    std::optional<std::string> username;
};

inline void to_json(nlohmann::json &j, const User &u)
{
    if (u.additionalGids)
    {
        j["additionalGids"] = u.additionalGids;
    }
    if (u.gid)
    {
        j["gid"] = u.gid;
    }
    if (u.uid)
    {
        j["uid"] = u.uid;
    }
    if (u.umask)
    {
        j["umask"] = u.umask;
    }
    if (u.username)
    {
        j["username"] = u.username;
    }
}

struct Process
{
    std::string cwd;
    std::vector<std::string> args;
    std::vector<std::string> env;
    std::optional<bool> terminal;
    std::optional<User> user;
};

inline void to_json(nlohmann::json &j, const Process &p)
{
    j["cwd"] = p.cwd;
    if (p.terminal)
    {
        j["terminal"] = p.terminal;
    }
    if (p.user)
    {
        j["user"] = p.user.value();
    }
    j["args"] = p.args;
    j["env"] = p.env;
}

struct Hook
{
    std::string path;
    std::optional<std::vector<std::string>> args;
    std::optional<std::vector<std::string>> env;
};

inline void to_json(nlohmann::json &j, const Hook &h)
{
    j["path"] = h.path;
    if (h.args)
    {
        j["args"] = h.args;
    }
    if (h.env)
    {
        j["env"] = h.env;
    }
}

struct Hooks
{
    std::optional<std::vector<Hook>> prestart;
    std::optional<std::vector<Hook>> createRuntime;
    std::optional<std::vector<Hook>> createContainer;
    std::optional<std::vector<Hook>> startContainer;
    std::optional<std::vector<Hook>> postStart;
    std::optional<std::vector<Hook>> postStop;
};

inline void to_json(nlohmann::json &j, const Hooks &h)
{
    if (h.prestart)
    {
        j["prestart"] = h.prestart;
    }
    if (h.createRuntime)
    {
        j["createRuntime"] = h.createRuntime;
    }
    if (h.createContainer)
    {
        j["createContainer"] = h.createContainer;
    }
    if (h.startContainer)
    {
        j["startContainer"] = h.startContainer;
    }
    if (h.postStart)
    {
        j["poststart"] = h.postStart;
    }
    if (h.postStop)
    {
        j["poststop"] = h.postStop;
    }
}

struct IDMap
{
    uint64_t containerID = 0;
    uint64_t hostID = 0;
    uint64_t size = 0;
};

inline void to_json(nlohmann::json &j, const IDMap &i)
{
    j["hostID"] = i.hostID;
    j["containerID"] = i.containerID;
    j["size"] = i.size;
}

struct SyscallArg
{
    uint index;        // require
    uint64_t value;    // require
    uint64_t valueTwo; // optional
    std::string op;    // require
};

inline void to_json(nlohmann::json &j, const SyscallArg &s)
{
    j["index"] = s.index;
    j["value"] = s.value;
    j["valueTwo"] = s.valueTwo;
    j["op"] = s.op;
}

struct Syscall
{
    std::vector<std::string> names;
    std::vector<std::string> args;
    std::string action;
};

inline void to_json(nlohmann::json &j, const Syscall &s)
{
    j["names"] = s.names;
    j["action"] = s.action;
    j["args"] = s.args;
}

struct Seccomp
{
    std::string defaultAction = "INVALID_ACTION";
    std::vector<std::string> architectures;
    std::vector<Syscall> syscalls;
};

inline void to_json(nlohmann::json &j, const Seccomp &s)
{
    j["defaultAction"] = s.defaultAction;
    j["architectures"] = s.architectures;
    j["syscalls"] = s.syscalls;
}

struct ResourceMemory
{
    int64_t limit = -1;
    int64_t reservation = -1;
    int64_t swap = -1;
};

inline void to_json(nlohmann::json &j, const ResourceMemory &r)
{
    j["limit"] = r.limit;
    j["reservation"] = r.reservation;
    j["swap"] = r.swap;
}

struct ResourceCPU
{
    uint64_t shares = 1024;
    int64_t quota = 100000;
    uint64_t period = 100000;
    int64_t realtimeRuntime;
    int64_t realtimePeriod;
    std::string cpus;
    std::string mems;
};

inline void to_json(nlohmann::json &j, const ResourceCPU &c)
{
    j["shares"] = c.shares;
    j["quota"] = c.quota;
    j["period"] = c.period;
    j["realtimeRuntime"] = c.realtimeRuntime;
    j["realtimePeriod"] = c.realtimePeriod;
    j["cpus"] = c.cpus;
    j["mems"] = c.mems;
}

struct Resources
{
    ResourceMemory memory;
    ResourceCPU cpu;
};

inline void to_json(nlohmann::json &j, const Resources &r)
{
    j["cpu"] = r.cpu;
    j["memory"] = r.memory;
}

struct NameSpace
{
    std::string linuxNamespace;
};

inline void to_json(nlohmann::json &j, const NameSpace &n)
{
    j = nlohmann::json::object();
    j["type"] = n.linuxNamespace;
}

struct Linux
{
    std::vector<NameSpace> namespaces;
    std::vector<IDMap> uidMappings;
    std::vector<IDMap> gidMappings;
    std::optional<Seccomp> seccomp;
    std::optional<Resources> resources;
    std::string cgroupsPath;
};

inline void to_json(nlohmann::json &j, const Linux &l)
{
    j["namespaces"] = l.namespaces;
    j["uidMappings"] = l.uidMappings;
    j["gidMappings"] = l.gidMappings;
    j["cgroupsPath"] = l.cgroupsPath;
    if (l.seccomp)
    {
        j["seccomp"] = l.seccomp;
    }
    if (l.resources)
    {
        j["resources"] = l.resources;
    }
}

struct Mount
{
    std::string source;
    std::string destination;
    std::string type;
    std::optional<std::vector<std::string>> options;
};

inline void to_json(nlohmann::json &j, const Mount &m)
{
    j["source"] = m.source;
    j["destination"] = m.destination;
    j["type"] = m.type;
    if (m.options)
    {
        j["options"] = m.options;
    }
}

struct KMConfigDbata
{
    std::optional<std::string> ociVersion;
    std::optional<std::string> hostname;
    std::optional<std::string> domainname;
    std::optional<std::map<std::string, std::string>> annotations;
    std::optional<Hooks> hooks;
    std::optional<Linux> linuxConfig;
    std::optional<Process> process;
    std::optional<Root> root;
    std::optional<std::vector<Mount>> mounts;
};

inline void to_json(nlohmann::json &j, const KMConfigDbata &c)
{
    if (c.ociVersion)
    {
        j["ociVersion"] = c.ociVersion;
    }

    if (c.hostname)
    {
        j["hostname"] = c.hostname;
    }

    if (c.domainname)
    {
        j["domainname"] = c.domainname;
    }

    if (c.annotations)
    {
        j["annotations"] = c.annotations;
    }

    if (c.root)
    {
        j["root"] = c.root;
    }

    if (c.hooks)
    {
        j["hooks"] = c.hooks;
    }

    if (c.process)
    {
        j["process"] = c.process;
    }

    if (c.linuxConfig)
    {
        j["linux"] = c.linuxConfig;
    }

    if (c.mounts)
    {
        j["mounts"] = c.mounts;
    }
}

// 容器状态枚举值
enum class Status : int
{
    Created,
    Creating,
    Running,
    Stopped
};

inline void from_json(const nlohmann::json &j, Status &s)
{
    if (j == "created")
    {
        s = Status::Created;
    }
    else if (j == "creating")
    {
        s = Status::Creating;
    }
    else if (j == "running")
    {
        s = Status::Running;
    }
    else if (j == "stopped")
    {
        s = Status::Stopped;
    }
}

// 容器状态
struct State
{
    Status status;
    std::optional<std::map<std::string, std::string>> annotations;
    std::string bundle;
    std::string id;
    std::string ociVersion;
    std::optional<int64_t> pid;
};

inline void from_json(const nlohmann::json &j, State &x)
{
    x.annotations = j.at("annotations").get<std::map<std::string, std::string>>();
    x.bundle = j.at("bundle").get<std::string>();
    x.id = j.at("id").get<std::string>();
    x.ociVersion = j.at("ociVersion").get<std::string>();
    x.pid = j.at("pid").get<int64_t>();
    x.status = j.at("status").get<Status>();
}

// crun list 命令输出的容器信息
struct ContainerListItem
{
    int64_t pid;
    std::string bundle;
    std::string created;
    std::string id;
    std::string owner;
    std::string status;
};

inline void from_json(const nlohmann::json &j, ContainerListItem &x)
{
    x.bundle = j.at("bundle").get<std::string>();
    x.created = j.at("created").get<std::string>();
    x.id = j.at("id").get<std::string>();
    x.owner = j.at("owner").get<std::string>();
    x.pid = j.at("pid").get<int64_t>();
    x.status = j.at("status").get<std::string>();
}

#endif // __KMCONFIG_DATA_H__