using System.Reflection;

namespace IamService.Domain.SeedWork;

/// <summary>
/// EN: Base class for enumeration classes (type-safe enum pattern).
/// VI: Lớp cơ sở cho các lớp enumeration (pattern enum an toàn kiểu).
/// </summary>
/// <remarks>
/// EN: This provides a type-safe alternative to enums with additional functionality
/// like validation, parsing, and rich behavior.
/// VI: Cung cấp một thay thế an toàn kiểu cho enums với các chức năng bổ sung
/// như validation, parsing, và hành vi phong phú.
/// </remarks>
public abstract class Enumeration : IComparable
{
    /// <summary>
    /// EN: The name of the enumeration value.
    /// VI: Tên của giá trị enumeration.
    /// </summary>
    public string Name { get; private set; }

    /// <summary>
    /// EN: The unique identifier of the enumeration value.
    /// VI: Định danh duy nhất của giá trị enumeration.
    /// </summary>
    public int Id { get; private set; }

    protected Enumeration(int id, string name) => (Id, Name) = (id, name);

    public override string ToString() => Name;

    /// <summary>
    /// EN: Get all enumeration values of a given type.
    /// VI: Lấy tất cả các giá trị enumeration của một kiểu cho trước.
    /// </summary>
    public static IEnumerable<T> GetAll<T>() where T : Enumeration =>
        typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly)
            .Select(f => f.GetValue(null))
            .Cast<T>();

    public override bool Equals(object? obj)
    {
        if (obj is not Enumeration otherValue)
            return false;

        var typeMatches = GetType() == obj.GetType();
        var valueMatches = Id.Equals(otherValue.Id);

        return typeMatches && valueMatches;
    }

    public override int GetHashCode() => Id.GetHashCode();

    /// <summary>
    /// EN: Get absolute difference between two enumeration values.
    /// VI: Lấy sự khác biệt tuyệt đối giữa hai giá trị enumeration.
    /// </summary>
    public static int AbsoluteDifference(Enumeration firstValue, Enumeration secondValue)
    {
        return Math.Abs(firstValue.Id - secondValue.Id);
    }

    /// <summary>
    /// EN: Parse an integer ID to the corresponding enumeration value.
    /// VI: Parse một ID integer thành giá trị enumeration tương ứng.
    /// </summary>
    public static T FromValue<T>(int value) where T : Enumeration
    {
        var matchingItem = Parse<T, int>(value, "value", item => item.Id == value);
        return matchingItem;
    }

    /// <summary>
    /// EN: Parse a display name to the corresponding enumeration value.
    /// VI: Parse một tên hiển thị thành giá trị enumeration tương ứng.
    /// </summary>
    public static T FromDisplayName<T>(string displayName) where T : Enumeration
    {
        var matchingItem = Parse<T, string>(displayName, "display name", item => item.Name == displayName);
        return matchingItem;
    }

    private static T Parse<T, TValue>(TValue value, string description, Func<T, bool> predicate) where T : Enumeration
    {
        var matchingItem = GetAll<T>().FirstOrDefault(predicate);

        if (matchingItem is null)
            throw new InvalidOperationException($"'{value}' is not a valid {description} in {typeof(T)}");

        return matchingItem;
    }

    public int CompareTo(object? other) => Id.CompareTo(((Enumeration)other!).Id);
}
