/**
 * @file semver.hpp
 * @author yushuoqi (yushuoqi@kylinos.cn)
 * @brief 4段式标准 Semver 2.0 版本规范封装
 * 
 * @version 0.1
 * @date 2024-09-10
 * 
 * 使用技巧：
 *      1. 将本文件添加到项目头文件列表
 *      2. 进行比较版本号大小
 * ```
 *   Semver::KMVersion ver = Semver::KMVersion::parse("1.2.3.0-alpha.b.3+build");
 *   Semver::KMVersion ver2 = Semver::KMVersion::parse("1.2.3.0");
 *   kmlogger.info("version=> major:%ld minor:%ld patch:%ld secure:%ld prerelease:%s buildmeta:%s", ver.major(), ver.minor(), ver.patch(), ver.secure(), ver.prerelease(), ver.buildMeta());
 *
 *   if (ver2 > ver)
 *   {
 *       kmlogger.info("version compare true");
 *   }
 * 
 * ```
 * @copyright Copyright (c) 2024
 * 
 */
#ifndef SEMVER_H
#define SEMVER_H

#ifndef SEMVER_MODULE
#include <ostream>
#include <string>
#include <regex>
#include <utility>
#include <vector>

// conditionally include <format> and its dependency <string_view> for C++20
#ifdef __cpp_lib_format
#if __cpp_lib_format >= 201907L
#include <format>
#include <string_view>
#endif
#endif
#endif

#ifdef SEMVER_MODULE
#define SEMVER_EXPORT export
#else
#define SEMVER_EXPORT
#endif

namespace Semver
{
    const std::string defaultPrereleasePart = "0";
    const std::string numbers = "0123456789";
    const std::string prereleaseAllowedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-";
    const std::string versionPattern = "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)"
                                       "(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))"
                                       "?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$";
    const std::string looseVersionPattern = "^v?(0|[1-9]\\d*)(?:\\.(0|[1-9]\\d*))?(?:\\.(0|[1-9]\\d*))?(?:\\.(0|[1-9]\\d*))"
                                            "?(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))"
                                            "?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$";

    SEMVER_EXPORT struct semverException : public std::runtime_error
    {
        explicit semverException(const std::string &message)
            : std::runtime_error(message)
        {
        }
    };

    /**
     * @brief 解析数字部分
     * 
     * @param versionPart 待解析的数字部分
     * @return uint64_t 返回解析后的数字部分
     */
    inline uint64_t parseNumericPart(const std::string &versionPart)
    {
        return static_cast<uint64_t>(std::stoull(versionPart));
    }

    /**
     * @brief 用 delimiter 分隔符分割字符串 text
     * 
     * @param text 待分割的字符串
     * @param delimiter 分隔符
     * @return std::vector<std::string> 分割后的字符串
     */
    inline std::vector<std::string> split(const std::string &text, const char &delimiter)
    {
        std::size_t posStart = 0, posEnd, delimLen = 1;
        std::string current;
        std::vector<std::string> result;

        while ((posEnd = text.find(delimiter, posStart)) != std::string::npos)
        {
            current = text.substr(posStart, posEnd - posStart);
            posStart = posEnd + delimLen;
            result.push_back(current);
        }

        result.push_back(text.substr(posStart));
        return result;
    }

    /**
     * @brief 判断字符串是否是数字
     * 
     * @param text 待判断的字符串
     * @return true 当前字符串是数字
     * @return false 当前字符串不是数字
     */
    inline bool isNumeric(const std::string &text)
    {
        return text.find_first_not_of(numbers) == std::string::npos;
    }

    /**
     * @brief 判断当前字符串是否合法的先行版本号
     * 
     * @param text 待判断的字符串
     * @return true 当前字符串是否合法的先行版本号
     * @return false 当前字符串不是合法的先行版本号
     */
    inline bool isValidPrerelease(const std::string &text)
    {
        return text.find_first_not_of(numbers + prereleaseAllowedChars) == std::string::npos;
    }

    /**
     * @brief 先行号版本解析
     * 
     */
    class KMPrereleasePart
    {
    private:
        bool m_numeric = false;
        std::string m_value;
        uint64_t m_numericValue;

    public:
        explicit KMPrereleasePart(const std::string &part)
        {
            if (part.empty())
            {
                throw semverException("Pre-release identity contains an empty part.");
            }

            if (isNumeric(part))
            {
                if (part.size() > 1 && part[0] == '0')
                {
                    throw semverException("Pre-release part '" + part + "' is numeric but contains a leading zero.");
                }
                m_numericValue = parseNumericPart(part);
                m_numeric = true;
            }

            if (!isValidPrerelease(part))
            {
                throw semverException("Pre-release part '" + part + "' contains an invalid character.");
            }
            m_value = part;
        }

        bool numeric() const
        {
            return m_numeric;
        }

        std::string value() const
        {
            return m_value;
        }
        
        uint64_t getNumericValue() const
        {
            return m_numericValue;
        }

        int compare(const KMPrereleasePart &other) const
        {
            if (m_numeric && !other.m_numeric)
            {
                return -1;
            }

            if (!m_numeric && other.m_numeric)
            {
                return 1;
            }

            if (m_numeric)
            {
                return (m_numericValue < other.m_numericValue) ? -1 : (m_numericValue > other.m_numericValue);
            }
            return (m_value < other.m_value) ? -1 : (m_value > other.m_value);
        }
    };

    /**
     * @brief 先行号版本
     * 
     */
    SEMVER_EXPORT class KMPrereleaseDescriptor
    {
    private:
        std::vector<KMPrereleasePart> m_parts;
        std::string m_prereleaseString;

        explicit KMPrereleaseDescriptor(const std::vector<KMPrereleasePart> &parts)
            : m_parts(parts)
        {
            if (parts.empty())
            {
                m_prereleaseString = "";
            }

            for (const auto &part : parts)
            {
                if (!m_prereleaseString.empty())
                {
                    m_prereleaseString += ".";
                }

                m_prereleaseString += part.value();
            }
        }

    public:
        std::string str() const
        {
            return m_prereleaseString;
        }

        bool isEmpty() const
        {
            return m_parts.empty();
        }

        std::string identity() const
        {
            if (isEmpty())
            {
                return "";
            }

            return m_parts.front().value();
        }

        KMPrereleaseDescriptor increment() const
        {
            std::vector<KMPrereleasePart> newparts = (m_parts);
            size_t lastNumericIndex = 0;
            bool lastNumericIndexFound = false;
            for (size_t i = 0; i < newparts.size(); ++i)
            {
                if (newparts[i].numeric())
                {
                    lastNumericIndex = i;
                    lastNumericIndexFound = true;
                }
            }

            if (lastNumericIndexFound)
            {
                KMPrereleasePart last = newparts[lastNumericIndex];
                newparts[lastNumericIndex] = KMPrereleasePart(std::to_string(last.getNumericValue() + 1));
            }
            else
            {
                newparts.emplace_back(defaultPrereleasePart);
            }
            return KMPrereleaseDescriptor(newparts);
        }

        int compare(const KMPrereleaseDescriptor &other) const
        {
            auto thisSize = m_parts.size();
            auto otherSize = other.m_parts.size();

            auto count = std::min(thisSize, otherSize);
            for (size_t i = 0; i < count; ++i)
            {
                int cmp = m_parts[i].compare(other.m_parts[i]);
                if (cmp != 0)
                {
                    return cmp;
                }
            }
            return (thisSize < otherSize) ? -1 : (thisSize > otherSize);
        }

        bool operator<(const KMPrereleaseDescriptor &other) const
        {
            return compare(other) == -1;
        }

        bool operator>(const KMPrereleaseDescriptor &other) const
        {
            return (other < *this);
        }

        bool operator==(const KMPrereleaseDescriptor &other) const
        {
            return m_prereleaseString == other.m_prereleaseString;
        }

        bool operator!=(const KMPrereleaseDescriptor &other) const
        {
            return m_prereleaseString != other.m_prereleaseString;
        }

        static KMPrereleaseDescriptor parse(const std::string &prerelease_part_str)
        {
            if (prerelease_part_str.empty())
            {
                return empty();
            }

            std::vector<KMPrereleasePart> prereleaseParts;
            std::vector<std::string> parts = split(prerelease_part_str, '.');
            for (auto &part : parts)
            {
                prereleaseParts.emplace_back(part);
            }
            return KMPrereleaseDescriptor(prereleaseParts);
        }

        static KMPrereleaseDescriptor empty()
        {
            return KMPrereleaseDescriptor({});
        }

        static KMPrereleaseDescriptor initial()
        {
            return KMPrereleaseDescriptor::parse(defaultPrereleasePart);
        }
    };

    SEMVER_EXPORT enum inc { major, minor, patch, secure, prerelease };

    /**
     * @brief 解析 Semver 2.0 标准版本4段式规范
     * 
     */
    SEMVER_EXPORT class KMVersion
    {
    private:
        uint64_t m_major;
        uint64_t m_minor;
        uint64_t m_patch;
        uint64_t m_secure;
        KMPrereleaseDescriptor m_prerelease;
        std::string m_buildMeta;


    public:
        explicit KMVersion(uint64_t major = 0, uint64_t minor = 0, uint64_t patch = 0, uint64_t secure = 0, const std::string &prerelease = "", const std::string buildMeta = "")
            : m_major{ major }
            , m_minor{ minor }
            , m_patch{ patch }
            , m_secure{ secure }
            , m_prerelease{ KMPrereleaseDescriptor::parse(prerelease) }
            , m_buildMeta{ std::move(buildMeta) }
        {
        }

        uint64_t major() const
        {
            return m_major;
        }

        uint64_t minor() const
        {
            return m_minor;
        }

        uint64_t patch() const
        {
            return m_patch;
        }

        uint64_t secure() const
        {
            return m_secure;
        }

        std::string prerelease() const
        {
            return m_prerelease.str();
        }

        std::string buildMeta() const
        {
            return m_buildMeta;
        }

        bool isPrerelease() const
        {
            return !m_prerelease.isEmpty();
        }

        /**
         * @brief 判断是否是稳定版本
         * 
         * @return true 是稳定版本，如　"1.0.0.0"
         * @return false 不是稳定版本，如　"1.0.0.0-alpha"
         */
        bool isStable() const
        {
            return m_major > 0 && m_prerelease.isEmpty();
        }

        /**
         * @brief 将字符串转变成符合规范的字符串
         * 
         * @return std::string 符合规范的字符串
         */
        std::string str() const
        {
            std::string result = std::to_string(m_major) + "." + std::to_string(m_minor) + "." + std::to_string(m_patch) + "." + std::to_string(m_secure);

            if (!m_prerelease.isEmpty())
            {
                result += "-" + m_prerelease.str();
            }

            if (!m_buildMeta.empty())
            {
                result += "+" + m_buildMeta;
            }

            return result;
        }

        /**
         * @brief 返回无后缀的稳定版本
         * 
         * @return KMVersion 无后缀的稳定版本
         */
        KMVersion withoutSuffixes() const
        {
            return KMVersion(m_major, m_minor, m_patch, m_secure);
        }

        /**
         * @brief 返回主版本号递增的版本
         * 
         * @param prerelease 先行版本号
         * @return KMVersion 主版本号递增的版本
         */
        KMVersion nextMajor(const std::string &prerelease = "") const
        {
            return KMVersion(m_major + 1, 0, 0, 0, prerelease);
        }

        /**
         * @brief 返回次版本号递增的版本
         * 
         * @param prerelease 先行版本号
         * @return KMVersion 次版本号递增的版本
         */
        KMVersion nextMinor(const std::string &prerelease = "") const
        {
            return KMVersion(m_major, m_minor + 1, 0, 0, prerelease);
        }

        /**
         * @brief 返回修订版本号递增的版本
         * 
         * @param prerelease 先行版本号
         * @return KMVersion 修订版本号递增的版本
         */
        KMVersion nextPatch(const std::string &prerelease = "") const
        {
            return KMVersion(m_major, m_minor, (!isPrerelease() || !prerelease.empty() ? m_patch + 1 : m_patch), 0, prerelease);
        }

        /**
         * @brief 返回扩展安全保留版本号递增的版本
         * 
         * @param prerelease 先行版本号
         * @return KMVersion 扩展安全保留版本号递增的版本
         */
        KMVersion nextSecure(const std::string &prerelease = "") const
        {
            return KMVersion(m_major, m_minor, m_patch, (!isPrerelease() || !prerelease.empty() ? m_secure + 1 : m_secure), prerelease);
        }

        /**
         * @brief 返回先行版本号递增的版本
         * 
         * @param prerelease 先行版本号
         * @return KMVersion 先行版本号递增的版本
         */
        KMVersion nextPrerelease(const std::string &prerelease = "") const
        {
            std::string pre = defaultPrereleasePart;
            if (!prerelease.empty())
            {
                pre = isPrerelease() && m_prerelease.identity() == prerelease ? m_prerelease.increment().str() : prerelease;
            }
            else if (prerelease.empty() && isPrerelease())
            {
                pre = m_prerelease.increment().str();
            }
            return KMVersion(m_major, m_minor, m_patch, isPrerelease() ? m_secure : m_secure + 1, pre);
        }

        /**
         * @brief 版本号递增
         * 
         * @param by 
         * @param prerelease 先行版本号
         * @return KMVersion 版本号递增后的版本号
         */
        KMVersion increment(inc by, const std::string &prerelease = "") const
        {
            switch (by)
            {
            case Semver::major:
                return nextMajor(prerelease);
            case Semver::minor:
                return nextMinor(prerelease);
            case Semver::patch:
                return nextPatch(prerelease);
            case Semver::secure:
                return nextSecure(prerelease);
            case Semver::prerelease:
                return nextPrerelease(prerelease);
            default:
                throw semverException("Invalid 'by' parameter in 'increment()' function.");
            }
        }

        int compare(const KMVersion &other) const
        {
            if (m_major > other.m_major)
            {
                return 1;
            }

            if (m_major < other.m_major)
            {
                return -1;
            }

            if (m_minor > other.m_minor)
            {
                return 1;
            }

            if (m_minor < other.m_minor)
            {
                return -1;
            }

            if (m_patch > other.m_patch)
            {
                return 1;
            }

            if (m_patch < other.m_patch)
            {
                return -1;
            }

            if (m_secure > other.m_secure)
            {
                return 1;
            }

            if (m_secure < other.m_secure)
            {
                return -1;
            }

            if (!m_prerelease.isEmpty() && other.m_prerelease.isEmpty())
            {
                return -1;
            }

            if (m_prerelease.isEmpty() && !other.m_prerelease.isEmpty())
            {
                return 1;
            }

            if (!m_prerelease.isEmpty() && !other.m_prerelease.isEmpty())
            {
                return m_prerelease.compare(other.m_prerelease);
            }

            return 0;
        }

        bool operator<(const KMVersion &other) const
        {
            return compare(other) == -1;
        }

        bool operator<=(const KMVersion &other) const
        {
            return compare(other) <= 0;
        }

        bool operator>(const KMVersion &other) const
        {
            return compare(other) == 1;
        }

        bool operator>=(const KMVersion &other) const
        {
            return compare(other) >= 0;
        }

        bool operator==(const KMVersion &other) const
        {
            return compare(other) == 0;
        }

        bool operator!=(const KMVersion &other) const
        {
            return compare(other) != 0;
        }

// conditionally provide three-way operator for C++20
#ifdef __cpp_impl_three_way_comparison
#if __cpp_impl_three_way_comparison >= 201907L

        auto operator<=>(const KMVersion &other) const
        {
            return compare(other);
        }

#endif
#endif

        /**
         * @brief 解析版本号字符串
         * 
         * @param versionString 待解析的版本号字符串
         * @param strict 是否是严格模式
         * @return KMVersion 解析后的版本号字符串
         */
        static KMVersion parse(const std::string &versionString, bool strict = true)
        {
            std::regex regex(strict ? versionPattern : looseVersionPattern);
            std::cmatch match;
            uint64_t major;
            uint64_t minor;
            uint64_t patch;
            uint64_t secure;
            std::string prerelease;
            std::string buildMeta;

            if (!std::regex_match(versionString.c_str(), match, regex))
            {
                throw semverException("Invalid KMVersion: " + versionString);
            }

            auto majorMatch = match[1];
            auto minorMatch = match[2];
            auto patchMatch = match[3];
            auto secureMatch = match[4];
            auto prereleaseMatch = match[5];
            auto buildMetaMatch = match[6];

            if (prereleaseMatch.matched)
            {
                prerelease = prereleaseMatch.str();
            }

            if (buildMetaMatch.matched)
            {
                buildMeta = buildMetaMatch.str();
            }

            try
            {
                if (strict && majorMatch.matched && minorMatch.matched && patchMatch.matched && secureMatch.matched)
                {
                    major = parseNumericPart(majorMatch);
                    minor = parseNumericPart(minorMatch);
                    patch = parseNumericPart(patchMatch);
                    secure = parseNumericPart(secureMatch);
                }
                else if (!strict && majorMatch.matched)
                {
                    major = parseNumericPart(majorMatch);
                    minor = minorMatch.matched ? parseNumericPart(minorMatch) : 0;
                    patch = patchMatch.matched ? parseNumericPart(patchMatch) : 0;
                    secure = secureMatch.matched ? parseNumericPart(secureMatch) : 0;
                }
                else
                {
                    throw semverException("Invalid KMVersion: " + versionString);
                }

                return KMVersion(major, minor, patch, secure, prerelease, buildMeta);
            }
            catch (std::exception &exception)
            {
                throw semverException("Version parse error: " + std::string(exception.what()));
            }
        }
    };

    SEMVER_EXPORT inline std::ostream &operator<<(std::ostream &str, const KMVersion &KMVersion)
    {
        for (const auto s : KMVersion.str())
        {
            str.put(s);
        }
        return str;
    }

    namespace literals
    {
        SEMVER_EXPORT inline KMVersion operator""_v(const char *text, std::size_t length)
        {
            return KMVersion::parse(std::string(text, length));
        }

        SEMVER_EXPORT inline KMVersion operator""_lv(const char *text, std::size_t length)
        {
            return KMVersion::parse(std::string(text, length), false);
        }
    } // namespace literals
} // namespace Semver

// conditionally provide formatter for C++20
#ifdef __cpp_lib_format
#if __cpp_lib_format >= 201907L

template<class CharT>
struct std::formatter<Semver::KMVersion, CharT> : std::formatter<std::string_view>
{
    template<class FormatContext>
    auto format(const Semver::KMVersion &KMVersion, FormatContext &ctx) const
    {
        return std::formatter<std::string_view>::format(KMVersion.str(), ctx);
    }
};

#endif
#endif

#endif // SEMVER_H
