namespace YMhut.Box.Core.Updates;

public static class UpdateVersionComparer
{
    public static string NormalizeVersion(string? version, string? build = null)
    {
        var baseParts = VersionParts(version).ToArray();
        if (baseParts.Length == 0)
        {
            return string.Empty;
        }

        if (!string.IsNullOrWhiteSpace(build) &&
            baseParts.Length < 4 &&
            TryReadFirstNumber(build, out var buildNumber))
        {
            baseParts = [.. baseParts, buildNumber];
        }

        return string.Join('.', baseParts);
    }

    public static int Compare(string? remoteVersion, string? remoteBuild, string? currentVersion)
    {
        return CompareNormalized(NormalizeVersion(remoteVersion, remoteBuild), NormalizeVersion(currentVersion));
    }

    public static int CompareNormalized(string? remoteVersion, string? currentVersion)
    {
        var remoteParts = VersionParts(remoteVersion).ToArray();
        var currentParts = VersionParts(currentVersion).ToArray();
        var length = Math.Max(remoteParts.Length, currentParts.Length);
        for (var index = 0; index < length; index++)
        {
            var left = index < remoteParts.Length ? remoteParts[index] : 0;
            var right = index < currentParts.Length ? currentParts[index] : 0;
            if (left > right)
            {
                return 1;
            }

            if (left < right)
            {
                return -1;
            }
        }

        return 0;
    }

    public static bool IsRemoteNewer(string? remoteVersion, string? remoteBuild, string? currentVersion)
    {
        return Compare(remoteVersion, remoteBuild, currentVersion) > 0;
    }

    private static IEnumerable<int> VersionParts(string? value)
    {
        return (value ?? string.Empty)
            .Split(['.', '+', '-', ' ', '_'], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
            .Select(ReadLeadingNumber)
            .Where(part => part is not null)
            .Select(part => part!.Value);
    }

    private static int? ReadLeadingNumber(string part)
    {
        var digits = new string(part.TakeWhile(char.IsDigit).ToArray());
        return int.TryParse(digits, out var parsed) ? parsed : null;
    }

    private static bool TryReadFirstNumber(string value, out int number)
    {
        number = 0;
        foreach (var part in VersionParts(value))
        {
            number = part;
            return true;
        }

        return false;
    }
}
