using System.Globalization;

namespace StellaOps.Feedser.Normalization.Distro;

/// <summary>
/// Represents a parsed NEVRA (Name-Epoch:Version-Release.Architecture) identifier and exposes helpers for canonical formatting.
/// </summary>
public sealed class Nevra
{
    private Nevra(string name, int epoch, bool hasExplicitEpoch, string version, string release, string? architecture, string original)
    {
        Name = name;
        Epoch = epoch;
        HasExplicitEpoch = hasExplicitEpoch;
        Version = version;
        Release = release;
        Architecture = architecture;
        Original = original;
    }

    /// <summary>
    /// Package name segment.
    /// </summary>
    public string Name { get; }

    /// <summary>
    /// Epoch extracted from the NEVRA string (defaults to <c>0</c> when omitted).
    /// </summary>
    public int Epoch { get; }

    /// <summary>
    /// Indicates whether an epoch segment was present explicitly (e.g. <c>0:</c>).
    /// </summary>
    public bool HasExplicitEpoch { get; }

    /// <summary>
    /// Version component (without epoch or release).
    /// </summary>
    public string Version { get; }

    /// <summary>
    /// Release component (without architecture suffix).
    /// </summary>
    public string Release { get; }

    /// <summary>
    /// Optional architecture suffix (e.g. <c>x86_64</c>, <c>noarch</c>).
    /// </summary>
    public string? Architecture { get; }

    /// <summary>
    /// Trimmed NEVRA string supplied to <see cref="TryParse"/>.
    /// </summary>
    public string Original { get; }

    private static readonly ISet<string> KnownArchitectures = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
    {
        "noarch",
        "src",
        "nosrc",
        "x86_64",
        "aarch64",
        "armv7hl",
        "armhfp",
        "ppc64",
        "ppc64le",
        "ppc",
        "s390",
        "s390x",
        "i386",
        "i486",
        "i586",
        "i686",
        "amd64",
        "arm64",
        "armv7l",
        "armv6l",
        "armv8l",
        "armel",
        "armhf",
        "ia32e",
        "loongarch64",
        "mips",
        "mips64",
        "mips64le",
        "mipsel",
        "ppc32",
        "ppc64p7",
        "riscv64",
        "sparc",
        "sparc64"
    };

    /// <summary>
    /// Attempts to parse the provided value into a <see cref="Nevra"/> instance.
    /// </summary>
    public static bool TryParse(string? value, out Nevra? result)
    {
        result = null;
        if (string.IsNullOrWhiteSpace(value))
        {
            return false;
        }

        var trimmed = value.Trim();
        var releaseSeparator = trimmed.LastIndexOf('-');
        if (releaseSeparator <= 0 || releaseSeparator >= trimmed.Length - 1)
        {
            return false;
        }

        var releasePart = trimmed[(releaseSeparator + 1)..];
        var nameVersionPart = trimmed[..releaseSeparator];

        var versionSeparator = nameVersionPart.LastIndexOf('-');
        if (versionSeparator <= 0 || versionSeparator >= nameVersionPart.Length)
        {
            return false;
        }

        var versionPart = nameVersionPart[(versionSeparator + 1)..];
        var namePart = nameVersionPart[..versionSeparator];

        if (string.IsNullOrWhiteSpace(namePart))
        {
            return false;
        }

        string? architecture = null;
        var release = releasePart;
        var architectureSeparator = releasePart.LastIndexOf('.');
        if (architectureSeparator > 0 && architectureSeparator < releasePart.Length - 1)
        {
            var possibleArch = releasePart[(architectureSeparator + 1)..];
            if (KnownArchitectures.Contains(possibleArch))
            {
                architecture = possibleArch;
                release = releasePart[..architectureSeparator];
            }
        }

        var version = versionPart;
        var epoch = 0;
        var hasExplicitEpoch = false;
        var epochSeparator = versionPart.IndexOf(':');
        if (epochSeparator >= 0)
        {
            hasExplicitEpoch = true;
            var epochPart = versionPart[..epochSeparator];
            version = epochSeparator < versionPart.Length - 1 ? versionPart[(epochSeparator + 1)..] : string.Empty;

            if (epochPart.Length > 0 && !int.TryParse(epochPart, NumberStyles.Integer, CultureInfo.InvariantCulture, out epoch))
            {
                return false;
            }
        }

        if (string.IsNullOrWhiteSpace(version))
        {
            return false;
        }

        result = new Nevra(namePart, epoch, hasExplicitEpoch, version, release, architecture, trimmed);
        return true;
    }

    /// <summary>
    /// Parses the provided value into a <see cref="Nevra"/> or throws <see cref="FormatException"/>.
    /// </summary>
    public static Nevra Parse(string value)
    {
        if (!TryParse(value, out var nevra))
        {
            throw new FormatException($"Input '{value}' is not a valid NEVRA string.");
        }

        return nevra!;
    }

    /// <summary>
    /// Returns a canonical NEVRA string with trimmed components and normalized epoch/architecture placement.
    /// </summary>
    public string ToCanonicalString()
    {
        var epochSegment = HasExplicitEpoch || Epoch > 0 ? $"{Epoch}:" : string.Empty;
        var archSegment = string.IsNullOrWhiteSpace(Architecture) ? string.Empty : $".{Architecture}";
        return $"{Name}-{epochSegment}{Version}-{Release}{archSegment}";
    }

    /// <inheritdoc />
    public override string ToString() => Original;
}
