using System.Diagnostics.CodeAnalysis;
using PkmnLib.Static.Species;
using PkmnLib.Static.Utils;

namespace PkmnLib.Static.Libraries;

/// <summary>
/// The library for all species in the game.
/// </summary>
public interface IReadOnlySpeciesLibrary : IEnumerable<ISpecies>
{
    /// <summary>
    /// Tries to get a species from the library. Returns false if the species is not found.
    /// </summary>
    bool TryGet(StringKey key, [MaybeNullWhen(false)] out ISpecies value);

    /// <summary>
    /// Tried to get a species from the library by its national dex number. Returns false if the species is not found.
    /// </summary>
    bool TryGetById(int id, [MaybeNullWhen(false)] out ISpecies value);

    /// <summary>
    /// Gets a random species from the library.
    /// </summary>
    ISpecies GetRandom(IRandom random);

    /// <summary>
    /// The amount of species in the library.
    /// </summary>
    int Count { get; }

    /// <summary>
    /// Whether the library is empty.
    /// </summary>
    bool IsEmpty { get; }

    /// <summary>
    /// Finds the Pokémon that evolves into the given species. If the species does not evolve, returns null.
    /// If multiple species evolve into the given species, returns the first one found.
    /// </summary>
    ISpecies? FindPreEvolution(ISpecies species);
}

/// <inheritdoc cref="IReadOnlySpeciesLibrary"/>
public class SpeciesLibrary : DataLibrary<ISpecies>, IReadOnlySpeciesLibrary
{
    private Dictionary<ISpecies, ISpecies> _preEvolutionCache = new();

    /// <inheritdoc />
    public bool TryGetById(int id, [MaybeNullWhen(false)] out ISpecies value)
    {
        return this.FirstOrDefault(s => s.Id == id) is { } species
            ? (value = species) != null
            : (value = default) != null;
    }

    /// <inheritdoc />
    public ISpecies? FindPreEvolution(ISpecies species)
    {
        if (_preEvolutionCache.TryGetValue(species, out var preEvolution))
            return preEvolution;
        foreach (var s in this)
        {
            if (s.EvolutionData.All(e => e.ToSpecies != species.Name))
                continue;
            _preEvolutionCache[species] = s;
            return s;
        }
        return null;
    }
}