namespace PkmnLib.Dynamic.Models.Choices;

/// <summary>
/// Comparer for turnchoices, to determine the order in which they should be executed.
/// </summary>
public class TurnChoiceComparer : IComparer<ITurnChoice>
{
    /// <inheritdoc cref="TurnChoiceComparer"/>
    public static TurnChoiceComparer Instance { get; } = new();
    
    private enum CompareValues
    {
        XEqualsY = 0,
        XLessThanY = -1,
        XGreaterThanY = 1,
    }

    private static CompareValues CompareForSameType(ITurnChoice x, ITurnChoice y)
    {
        // Higher speed goes first
        var speedComparison = x.Speed.CompareTo(y.Speed);
        if (speedComparison != 0)
            return (CompareValues)speedComparison;
        // If speed is equal, we use the random values we've given to each choice to tiebreak.
        // This is to ensure that the order of choices is deterministic.
        return (CompareValues)x.RandomValue.CompareTo(y.RandomValue);
    }
    
    private static CompareValues CompareImpl(ITurnChoice? x, ITurnChoice? y)
    {
        // Deal with possible null values
        switch (x)
        {
            case null when y is null:
                return CompareValues.XEqualsY;
            case null:
                return CompareValues.XLessThanY;
        }
        if (y is null)
            return CompareValues.XGreaterThanY;

        switch (x)
        {
            case IMoveChoice moveX:
                // Move choices go first
                if (y is IMoveChoice moveY)
                {
                    // Higher priority goes first
                    var priorityComparison = moveX.Priority.CompareTo(moveY.Priority);
                    if (priorityComparison != 0)
                        return (CompareValues)priorityComparison;
                    return CompareForSameType(moveX, moveY);
                }
                return CompareValues.XGreaterThanY;
            case IItemChoice itemX:
                // Item choices go second
                return y switch
                {
                    IMoveChoice => CompareValues.XLessThanY,
                    IItemChoice itemY => CompareForSameType(itemX, itemY),
                    _ => CompareValues.XGreaterThanY,
                };
            case ISwitchChoice switchX:
                // Switch choices go third
                return y switch
                {
                    IMoveChoice or IItemChoice => CompareValues.XLessThanY,
                    ISwitchChoice switchY => CompareForSameType(switchX, switchY),
                    _ => CompareValues.XGreaterThanY,
                };
            case IPassChoice passX:
                // Pass choices go last
                return y switch
                {
                    IMoveChoice or IItemChoice or ISwitchChoice => CompareValues.XLessThanY,
                    IPassChoice passY => CompareForSameType(passX, passY),
                    _ => CompareValues.XGreaterThanY,
                };
        }
        
        return CompareValues.XLessThanY;
    }

    /// <inheritdoc />
    public int Compare(ITurnChoice? x, ITurnChoice? y) => (int) CompareImpl(x, y);
}