using System.Globalization;

namespace StellaOps.Feedser.Normalization.Distro;

/// <summary>
/// Represents a Debian epoch:version-revision tuple and exposes parsing/formatting helpers.
/// </summary>
public sealed class DebianEvr
{
    private DebianEvr(int epoch, bool hasExplicitEpoch, string version, string revision, string original)
    {
        Epoch = epoch;
        HasExplicitEpoch = hasExplicitEpoch;
        Version = version;
        Revision = revision;
        Original = original;
    }

    /// <summary>
    /// Epoch segment (defaults to <c>0</c> when omitted).
    /// </summary>
    public int Epoch { get; }

    /// <summary>
    /// Indicates whether an epoch segment was present explicitly.
    /// </summary>
    public bool HasExplicitEpoch { get; }

    /// <summary>
    /// Version portion (without revision).
    /// </summary>
    public string Version { get; }

    /// <summary>
    /// Revision portion (after the last dash). Empty when omitted.
    /// </summary>
    public string Revision { get; }

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

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

        var trimmed = value.Trim();
        var epoch = 0;
        var hasExplicitEpoch = false;
        var remainder = trimmed;

        var colonIndex = remainder.IndexOf(':');
        if (colonIndex >= 0)
        {
            if (colonIndex == 0)
            {
                return false;
            }

            var epochPart = remainder[..colonIndex];
            if (!int.TryParse(epochPart, NumberStyles.Integer, CultureInfo.InvariantCulture, out epoch))
            {
                return false;
            }

            hasExplicitEpoch = true;
            remainder = colonIndex < remainder.Length - 1 ? remainder[(colonIndex + 1)..] : string.Empty;
        }

        if (string.IsNullOrEmpty(remainder))
        {
            return false;
        }

        var version = remainder;
        var revision = string.Empty;

        var dashIndex = remainder.LastIndexOf('-');
        if (dashIndex > 0)
        {
            version = remainder[..dashIndex];
            revision = dashIndex < remainder.Length - 1 ? remainder[(dashIndex + 1)..] : string.Empty;
        }

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

        result = new DebianEvr(epoch, hasExplicitEpoch, version, revision, trimmed);
        return true;
    }

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

        return evr!;
    }

    /// <summary>
    /// Returns a canonical EVR string with trimmed components and normalized epoch/revision placement.
    /// </summary>
    public string ToCanonicalString()
    {
        var epochSegment = HasExplicitEpoch || Epoch > 0 ? $"{Epoch}:" : string.Empty;
        var revisionSegment = string.IsNullOrEmpty(Revision) ? string.Empty : $"-{Revision}";
        return $"{epochSegment}{Version}{revisionSegment}";
    }

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