namespace Spectre.Console.Cli;

internal static class CommandSuggestor
{
    private const float SmallestDistance = 2f;

    public static CommandInfo? Suggest(CommandModel model, CommandInfo? command, string name)
    {
        var result = (CommandInfo?)null;

        var container = command ?? (ICommandContainer)model;
        if (command?.IsDefaultCommand ?? false)
        {
            // Default commands have no children,
            // so use the root commands here.
            container = model;
        }

        var score = float.MaxValue;
        foreach (var child in container.Commands.Where(x => !x.IsHidden))
        {
            var temp = Score(child.Name, name);
            if (temp < score)
            {
                score = temp;
                result = child;
            }
        }

        if (score <= SmallestDistance)
        {
            return result;
        }

        return null;
    }

    private static float Score(string source, string target)
    {
        source = source.ToUpperInvariant();
        target = target.ToUpperInvariant();

        var n = source.Length;
        var m = target.Length;

        if (n == 0)
        {
            return m;
        }

        if (m == 0)
        {
            return n;
        }

        var d = new int[n + 1, m + 1];
        Enumerable.Range(0, n + 1).ToList().ForEach(i => d[i, 0] = i);
        Enumerable.Range(0, m + 1).ToList().ForEach(i => d[0, i] = i);

        for (var sourceIndex = 1; sourceIndex <= n; sourceIndex++)
        {
            for (var targetIndex = 1; targetIndex <= m; targetIndex++)
            {
                var cost = (target[targetIndex - 1] == source[sourceIndex - 1]) ? 0 : 1;
                d[sourceIndex, targetIndex] = Math.Min(
                    Math.Min(d[sourceIndex - 1, targetIndex] + 1, d[sourceIndex, targetIndex - 1] + 1),
                    d[sourceIndex - 1, targetIndex - 1] + cost);
            }
        }

        return d[n, m];
    }
}