/* common/KMInfoJson.cpp
 *
 * Copyright (C) 2024 OpenKylin, Inc
 */

#include "KMInfoJson.h"
#include <fstream>
#include <filesystem>
#include <regex>
#include <iterator>
#include "KMException.h"
#include "KMUtil.h"

namespace fs = std::filesystem;

void KMInfoJson::InfoBase::baseParse()
{
    size_t beginPosId = m_baseString.find_first_of(":", 0);
    size_t endPosId = m_baseString.find_first_of("/", beginPosId);
    m_baseId = m_baseString.substr(beginPosId + 1, endPosId - beginPosId - 1);
    size_t beginPosVersion = endPosId + 1;
    size_t endPosVersion = m_baseString.find_first_of("/", beginPosVersion);
    m_baseVersion = m_baseString.substr(beginPosVersion, endPosVersion - beginPosVersion);
}

void KMInfoJson::InfoBase::versionParse()
{
    size_t firstPos = m_baseVersion.find_first_of(".", 0);
    if (firstPos != std::string::npos)
    {
        m_versionMajor = m_baseVersion.substr(0, firstPos);
        size_t secondPos = m_baseVersion.find_first_of(".", firstPos + 1);
        if (secondPos != std::string::npos)
        {
            m_versionMinor = m_baseVersion.substr(firstPos + 1, secondPos - firstPos - 1);
        }
    }
    else
    {
        m_versionMajor = m_baseVersion;
    }
}

bool KMInfoJson::InfoBase::checkVersion(const InfoBase &ver)
{
    if (m_versionMajor != ver.m_versionMajor)
    {
        return false;
    }

    if (m_versionMinor != "" && ver.m_versionMinor != "" && m_versionMinor != ver.m_versionMinor)
    {
        return false;
    }
    return true;
}

void KMInfoJson::loadFile(const std::string &jsonFile)
{
    m_root = KMNlohmannJsonHelper::loadFile(jsonFile);

    fromJson();
}

void KMInfoJson::saveFile(const std::string &jsonFile)
{
    fs::path baseDir = fs::path(jsonFile).parent_path();
    if (!fs::exists(baseDir))
    {
        std::error_code e;
        if (!KMUtil::mkpath(baseDir.string(), e))
        {
            throw KMException(e.message());
        }
    }

    toJson();

    std::ofstream o(jsonFile);
    o << std::setw(4) << m_root << std::endl;
}

void KMInfoJson::fromJson()
{
    KMNlohmannJsonHelper helper(m_root);
    id = helper.getStringValue("id");
    archs = helper.getStringListValue("arch");
    base = helper.getStringValue("base");
    runtime = helper.getStringValue("runtime");
    channel = helper.getStringValue("channel");
    commands = helper.getStringListValue("command");
    name = helper.getStringValue("name");
    description = helper.getStringValue("description");
    kind = helper.getStringValue("kind");
    module = helper.getStringValue("module");
    size = helper.getUInt64Value("size");
    version = helper.getStringValue("version");
    envs = helper.getStringMapValue("env");
    permissions = helper.getStringListValue("permission");
    license = helper.getStringValue("license");
    homepage = helper.getStringValue("homepage");
    maintainers = helper.getStringListValue("maintainer");

    // name[zh_CN]
    names = helper.fromPrefix("name[");
    // description[zh_CN]
    descriptions = helper.fromPrefix("description[");
}

void KMInfoJson::toJson()
{
    m_root["id"] = id;
    m_root["base"] = base;
    m_root["runtime"] = runtime;

    KMNlohmannJsonHelper helper(m_root);
    helper.setStringValue("channel", channel);
    helper.setStringValue("name", name);
    helper.setStringValue("description", description);
    helper.setStringValue("kind", kind);
    helper.setStringValue("module", module);
    helper.setUInt64Value("size", size);
    helper.setStringValue("version", version);
    helper.setStringValue("license", license);
    helper.setStringValue("homepage", homepage);

    helper.setStringListValue("arch", archs);
    helper.setStringListValue("command", commands);
    helper.add(names);
    helper.add(descriptions);
    helper.setStringMapValue("env", envs);
    helper.setStringListValue("permission", permissions);
    helper.setStringListValue("maintainer", maintainers);
}

KMRef KMInfoJson::baseRef() const
{
    return parseKMRef(base);
}

KMRef KMInfoJson::runtimeRef() const
{
    if (runtime.empty())
    {
        return KMRef();
    }

    return parseKMRef(runtime);
}

/**
 * @brief : 解析ref成KMRef
 * @param : [in] ref, 格式：${channel}:${base-id}/${base-version}/${arch}
 * @return: KMRef
 */
KMRef KMInfoJson::parseKMRef(const std::string &ref)
{
    std::regex reg{ "[:/]" };
    std::vector<std::string> splits{ std::sregex_token_iterator(ref.begin(), ref.end(), reg, -1), std::sregex_token_iterator() };

    if (splits.size() != 4)
    {
        throw KMException("The format of base and runtime must be : ${channel}:${base-id}/${base-version}/${arch}");
    }

    KMRef kmref = {
        .id = splits.at(1),
        .channel = splits.at(0),
        .version = splits.at(2),
        .arch = splits.at(3),
        .module = "",
        .branch = "",
        .kind = "",
        .refCommitId = "",
    };

    return kmref;
}
