using System;
using System.Collections.Generic;

namespace StellaOps.Feedser.Models;

/// <summary>
/// Optional structured representations of range semantics attached to <see cref="AffectedVersionRange"/>.
/// </summary>
public sealed record RangePrimitives(
    SemVerPrimitive? SemVer,
    NevraPrimitive? Nevra,
    EvrPrimitive? Evr,
    IReadOnlyDictionary<string, string>? VendorExtensions)
{
    public bool HasVendorExtensions => VendorExtensions is { Count: > 0 };

    public string GetCoverageTag()
    {
        var kinds = new List<string>(3);
        if (SemVer is not null)
        {
            kinds.Add("semver");
        }

        if (Nevra is not null)
        {
            kinds.Add("nevra");
        }

        if (Evr is not null)
        {
            kinds.Add("evr");
        }

        if (kinds.Count == 0)
        {
            return HasVendorExtensions ? "vendor" : "none";
        }

        kinds.Sort(StringComparer.Ordinal);
        return string.Join('+', kinds);
    }
}

/// <summary>
/// Structured SemVer metadata for a version range.
/// </summary>
public sealed record SemVerPrimitive(
    string? Introduced,
    bool IntroducedInclusive,
    string? Fixed,
    bool FixedInclusive,
    string? LastAffected,
    bool LastAffectedInclusive,
    string? ConstraintExpression,
    string? ExactValue = null)
{
    public string Style => DetermineStyle(
        Introduced,
        IntroducedInclusive,
        Fixed,
        FixedInclusive,
        LastAffected,
        LastAffectedInclusive,
        ConstraintExpression,
        ExactValue);

    private static string DetermineStyle(
        string? introduced,
        bool introducedInclusive,
        string? fixedVersion,
        bool fixedInclusive,
        string? lastAffected,
        bool lastAffectedInclusive,
        string? constraintExpression,
        string? exactValue)
    {
        if (!string.IsNullOrWhiteSpace(exactValue))
        {
            return SemVerPrimitiveStyles.Exact;
        }

        var hasIntroduced = !string.IsNullOrWhiteSpace(introduced);
        var hasFixed = !string.IsNullOrWhiteSpace(fixedVersion);
        var hasLast = !string.IsNullOrWhiteSpace(lastAffected);

        if (hasIntroduced && hasFixed)
        {
            return SemVerPrimitiveStyles.Range;
        }

        if (hasIntroduced)
        {
            return introducedInclusive
                ? SemVerPrimitiveStyles.GreaterThanOrEqual
                : SemVerPrimitiveStyles.GreaterThan;
        }

        if (hasFixed)
        {
            return fixedInclusive
                ? SemVerPrimitiveStyles.LessThanOrEqual
                : SemVerPrimitiveStyles.LessThan;
        }

        if (hasLast)
        {
            return lastAffectedInclusive
                ? SemVerPrimitiveStyles.LessThanOrEqual
                : SemVerPrimitiveStyles.LessThan;
        }

        return string.IsNullOrWhiteSpace(constraintExpression)
            ? SemVerPrimitiveStyles.Range
            : SemVerPrimitiveStyles.Range;
    }
}

public static class SemVerPrimitiveStyles
{
    public const string Range = "range";
    public const string Exact = "exact";
    public const string LessThan = "lessThan";
    public const string LessThanOrEqual = "lessThanOrEqual";
    public const string GreaterThan = "greaterThan";
    public const string GreaterThanOrEqual = "greaterThanOrEqual";
}

/// <summary>
/// Structured NEVRA metadata for a version range.
/// </summary>
public sealed record NevraPrimitive(
    NevraComponent? Introduced,
    NevraComponent? Fixed,
    NevraComponent? LastAffected);

/// <summary>
/// Structured Debian EVR metadata for a version range.
/// </summary>
public sealed record EvrPrimitive(
    EvrComponent? Introduced,
    EvrComponent? Fixed,
    EvrComponent? LastAffected);

/// <summary>
/// Normalized NEVRA component.
/// </summary>
public sealed record NevraComponent(
    string Name,
    int Epoch,
    string Version,
    string Release,
    string? Architecture)
{
    public string ToCanonicalString()
    {
        var epochSegment = Epoch > 0 ? $"{Epoch}:" : string.Empty;
        var architectureSegment = string.IsNullOrWhiteSpace(Architecture) ? string.Empty : $".{Architecture}";
        var releaseSegment = string.IsNullOrWhiteSpace(Release) ? string.Empty : Release;
        var releaseSuffix = string.IsNullOrEmpty(releaseSegment) ? string.Empty : $"-{releaseSegment}";
        return $"{Name}-{epochSegment}{Version}{releaseSuffix}{architectureSegment}";
    }
}

/// <summary>
/// Normalized EVR component (epoch:upstream revision).
/// </summary>
public sealed record EvrComponent(
    int Epoch,
    string UpstreamVersion,
    string? Revision)
{
    public string ToCanonicalString()
    {
        var epochSegment = Epoch > 0 ? $"{Epoch}:" : string.Empty;
        var revisionSegment = string.IsNullOrWhiteSpace(Revision) ? string.Empty : $"-{Revision}";
        return $"{epochSegment}{UpstreamVersion}{revisionSegment}";
    }
}
