using StellaOps.Feedser.Merge.Comparers;
using StellaOps.Feedser.Normalization.Distro;

namespace StellaOps.Feedser.Merge.Tests;

public sealed class NevraComparerTests
{
    [Theory]
    [InlineData("kernel-1:4.18.0-348.7.1.el8_5.x86_64", "kernel", 1, "4.18.0", "348.7.1.el8_5", "x86_64")]
    [InlineData("bash-5.1.8-2.fc35.x86_64", "bash", 0, "5.1.8", "2.fc35", "x86_64")]
    [InlineData("openssl-libs-1:1.1.1k-7.el8", "openssl-libs", 1, "1.1.1k", "7.el8", null)]
    [InlineData("java-11-openjdk-1:11.0.23.0.9-2.el9_4.ppc64le", "java-11-openjdk", 1, "11.0.23.0.9", "2.el9_4", "ppc64le")]
    [InlineData("bash-0:5.2.15-3.el9_4.arm64", "bash", 0, "5.2.15", "3.el9_4", "arm64")]
    [InlineData("podman-3:4.9.3-1.el9.x86_64", "podman", 3, "4.9.3", "1.el9", "x86_64")]
    public void TryParse_ReturnsExpectedComponents(string input, string expectedName, int expectedEpoch, string expectedVersion, string expectedRelease, string? expectedArch)
    {
        var success = Nevra.TryParse(input, out var nevra);

        Assert.True(success);
        Assert.NotNull(nevra);
        Assert.Equal(expectedName, nevra!.Name);
        Assert.Equal(expectedEpoch, nevra.Epoch);
        Assert.Equal(expectedVersion, nevra.Version);
        Assert.Equal(expectedRelease, nevra.Release);
        Assert.Equal(expectedArch, nevra.Architecture);
        Assert.Equal(input, nevra.Original);
    }

    [Theory]
    [InlineData("")]
    [InlineData("kernel4.18.0-80.el8")]
    [InlineData("kernel-4.18.0")]
    public void TryParse_InvalidInputs_ReturnFalse(string input)
    {
        var success = Nevra.TryParse(input, out var nevra);

        Assert.False(success);
        Assert.Null(nevra);
    }

    [Fact]
    public void TryParse_TrimsWhitespace()
    {
        var success = Nevra.TryParse("  kernel-0:4.18.0-80.el8.x86_64  ", out var nevra);

        Assert.True(success);
        Assert.NotNull(nevra);
        Assert.Equal("kernel", nevra!.Name);
        Assert.Equal("4.18.0", nevra.Version);
    }

    [Fact]
    public void Compare_PrefersHigherEpoch()
    {
        var older = "kernel-0:4.18.0-348.7.1.el8_5.x86_64";
        var newer = "kernel-1:4.18.0-348.7.1.el8_5.x86_64";

        Assert.True(NevraComparer.Instance.Compare(newer, older) > 0);
        Assert.True(NevraComparer.Instance.Compare(older, newer) < 0);
    }

    [Fact]
    public void Compare_UsesRpmVersionOrdering()
    {
        var lower = "kernel-0:4.18.0-80.el8.x86_64";
        var higher = "kernel-0:4.18.11-80.el8.x86_64";

        Assert.True(NevraComparer.Instance.Compare(higher, lower) > 0);
    }

    [Fact]
    public void Compare_UsesReleaseOrdering()
    {
        var el8 = "bash-0:5.1.0-1.el8.x86_64";
        var el9 = "bash-0:5.1.0-1.el9.x86_64";

        Assert.True(NevraComparer.Instance.Compare(el9, el8) > 0);
    }

    [Fact]
    public void Compare_TildeRanksEarlier()
    {
        var prerelease = "bash-0:5.1.0~beta-1.fc34.x86_64";
        var stable = "bash-0:5.1.0-1.fc34.x86_64";

        Assert.True(NevraComparer.Instance.Compare(prerelease, stable) < 0);
    }

    [Fact]
    public void Compare_ConsidersArchitecture()
    {
        var noarch = "pkg-0:1.0-1.noarch";
        var arch = "pkg-0:1.0-1.x86_64";

        Assert.True(NevraComparer.Instance.Compare(noarch, arch) < 0);
    }

    [Fact]
    public void Compare_FallsBackToOrdinalForInvalid()
    {
        var left = "not-a-nevra";
        var right = "also-not";

        var expected = Math.Sign(string.CompareOrdinal(left, right));
        var actual = Math.Sign(NevraComparer.Instance.Compare(left, right));
        Assert.Equal(expected, actual);
    }
}
