using System;
using System.Collections.Generic;

namespace StellaOps.Feedser.Models;

/// <summary>
/// Canonical normalized version rule emitted by range builders for analytical queries.
/// </summary>
public sealed record NormalizedVersionRule
{
    public NormalizedVersionRule(
        string scheme,
        string type,
        string? min = null,
        bool? minInclusive = null,
        string? max = null,
        bool? maxInclusive = null,
        string? value = null,
        string? notes = null)
    {
        Scheme = Validation.EnsureNotNullOrWhiteSpace(scheme, nameof(scheme)).ToLowerInvariant();
        Type = Validation.EnsureNotNullOrWhiteSpace(type, nameof(type)).Replace('_', '-').ToLowerInvariant();
        Min = Validation.TrimToNull(min);
        MinInclusive = minInclusive;
        Max = Validation.TrimToNull(max);
        MaxInclusive = maxInclusive;
        Value = Validation.TrimToNull(value);
        Notes = Validation.TrimToNull(notes);
    }

    public string Scheme { get; }

    public string Type { get; }

    public string? Min { get; }

    public bool? MinInclusive { get; }

    public string? Max { get; }

    public bool? MaxInclusive { get; }

    public string? Value { get; }

    public string? Notes { get; }
}

public sealed class NormalizedVersionRuleEqualityComparer : IEqualityComparer<NormalizedVersionRule>
{
    public static NormalizedVersionRuleEqualityComparer Instance { get; } = new();

    public bool Equals(NormalizedVersionRule? x, NormalizedVersionRule? y)
    {
        if (ReferenceEquals(x, y))
        {
            return true;
        }

        if (x is null || y is null)
        {
            return false;
        }

        return string.Equals(x.Scheme, y.Scheme, StringComparison.Ordinal)
            && string.Equals(x.Type, y.Type, StringComparison.Ordinal)
            && string.Equals(x.Min, y.Min, StringComparison.Ordinal)
            && x.MinInclusive == y.MinInclusive
            && string.Equals(x.Max, y.Max, StringComparison.Ordinal)
            && x.MaxInclusive == y.MaxInclusive
            && string.Equals(x.Value, y.Value, StringComparison.Ordinal)
            && string.Equals(x.Notes, y.Notes, StringComparison.Ordinal);
    }

    public int GetHashCode(NormalizedVersionRule obj)
        => HashCode.Combine(
            obj.Scheme,
            obj.Type,
            obj.Min,
            obj.MinInclusive,
            obj.Max,
            obj.MaxInclusive,
            obj.Value,
            obj.Notes);
}

public sealed class NormalizedVersionRuleComparer : IComparer<NormalizedVersionRule>
{
    public static NormalizedVersionRuleComparer Instance { get; } = new();

    public int Compare(NormalizedVersionRule? x, NormalizedVersionRule? y)
    {
        if (ReferenceEquals(x, y))
        {
            return 0;
        }

        if (x is null)
        {
            return -1;
        }

        if (y is null)
        {
            return 1;
        }

        var schemeComparison = string.Compare(x.Scheme, y.Scheme, StringComparison.Ordinal);
        if (schemeComparison != 0)
        {
            return schemeComparison;
        }

        var typeComparison = string.Compare(x.Type, y.Type, StringComparison.Ordinal);
        if (typeComparison != 0)
        {
            return typeComparison;
        }

        var minComparison = string.Compare(x.Min, y.Min, StringComparison.Ordinal);
        if (minComparison != 0)
        {
            return minComparison;
        }

        var minInclusiveComparison = NullableBoolCompare(x.MinInclusive, y.MinInclusive);
        if (minInclusiveComparison != 0)
        {
            return minInclusiveComparison;
        }

        var maxComparison = string.Compare(x.Max, y.Max, StringComparison.Ordinal);
        if (maxComparison != 0)
        {
            return maxComparison;
        }

        var maxInclusiveComparison = NullableBoolCompare(x.MaxInclusive, y.MaxInclusive);
        if (maxInclusiveComparison != 0)
        {
            return maxInclusiveComparison;
        }

        var valueComparison = string.Compare(x.Value, y.Value, StringComparison.Ordinal);
        if (valueComparison != 0)
        {
            return valueComparison;
        }

        return string.Compare(x.Notes, y.Notes, StringComparison.Ordinal);
    }

    private static int NullableBoolCompare(bool? x, bool? y)
    {
        if (x == y)
        {
            return 0;
        }

        return (x, y) switch
        {
            (null, not null) => -1,
            (not null, null) => 1,
            (false, true) => -1,
            (true, false) => 1,
            _ => 0,
        };
    }
}

public static class NormalizedVersionSchemes
{
    public const string SemVer = "semver";
    public const string Nevra = "nevra";
    public const string Evr = "evr";
}

public static class NormalizedVersionRuleTypes
{
    public const string Range = "range";
    public const string Exact = "exact";
    public const string LessThan = "lt";
    public const string LessThanOrEqual = "lte";
    public const string GreaterThan = "gt";
    public const string GreaterThanOrEqual = "gte";
}
