using PkmnLib.Static.Utils;

namespace PkmnLib.Static.Species;

/// <summary>
/// The storage of the moves a Pokémon can learn.
/// </summary>
public interface ILearnableMoves
{
    /// <summary>
    /// Adds a new level move the Pokémon can learn.
    /// </summary>
    /// <param name="level">The level the Pokémon learns the move at.</param>
    /// <param name="move">The move the Pokémon learns.</param>
    /// <returns>Whether the move was added successfully.</returns>
    void AddLevelMove(LevelInt level, StringKey move);

    /// <summary>
    /// Adds a new egg move the Pokémon can learn.
    /// </summary>
    /// <param name="move"></param>
    void AddEggMove(StringKey move);

    /// <summary>
    /// Gets all moves a Pokémon can learn when leveling up to a specific level.
    /// </summary>
    /// <param name="level">The level the Pokémon is learning moves at.</param>
    /// <returns>The moves the Pokémon learns at that level.</returns>
    IReadOnlyList<StringKey> GetLearnedByLevel(LevelInt level);

    /// <summary>
    /// Gets the distinct moves a Pokémon can learn through leveling up.
    /// </summary>
    /// <returns>The moves the Pokémon can learn through leveling up.</returns>
    IReadOnlyList<StringKey> GetDistinctLevelMoves();

    /// <summary>
    /// Gets a list of all moves a Pokémon can learn up to a specific level.
    /// </summary>
    IReadOnlyList<StringKey> GetLearnableMovesUpToLevel(LevelInt level);

    /// <summary>
    /// Gets all moves a Pokémon can learn by breeding.
    /// </summary>
    IReadOnlyList<StringKey> GetEggMoves();
}

/// <inheritdoc />
public class LearnableMovesImpl : ILearnableMoves
{
    private readonly Dictionary<LevelInt, List<StringKey>> _learnedByLevel = new();
    private readonly HashSet<StringKey> _distinctLevelMoves = new();

    private readonly List<StringKey> _eggMoves = new();

    /// <inheritdoc />
    public void AddLevelMove(LevelInt level, StringKey move)
    {
        if (!_learnedByLevel.TryGetValue(level, out var value))
            _learnedByLevel[level] = [move];
        else
            value.Add(move);
        _distinctLevelMoves.Add(move);
    }

    /// <inheritdoc />
    public void AddEggMove(StringKey move)
    {
        if (_eggMoves.Contains(move))
            return;
        _eggMoves.Add(move);
    }

    /// <inheritdoc />
    public IReadOnlyList<StringKey> GetLearnedByLevel(LevelInt level)
    {
        if (!_learnedByLevel.TryGetValue(level, out var value))
            return Array.Empty<StringKey>();
        return value;
    }

    /// <inheritdoc />
    public IReadOnlyList<StringKey> GetDistinctLevelMoves() => _distinctLevelMoves.ToList();

    /// <inheritdoc />
    public IReadOnlyList<StringKey> GetLearnableMovesUpToLevel(LevelInt level)
    {
        var moves = new HashSet<StringKey>();
        foreach (var kvp in _learnedByLevel.TakeWhile(kvp => kvp.Key <= level))
        {
            moves.UnionWith(kvp.Value);
        }
        return moves.ToList();
    }

    /// <inheritdoc />
    public IReadOnlyList<StringKey> GetEggMoves() => _eggMoves;
}