using System.Diagnostics.CodeAnalysis;
using PkmnLib.Dynamic.Models;
using PkmnLib.Static;
using PkmnLib.Static.Libraries;
using PkmnLib.Static.Species;

namespace PkmnLib.Dynamic.Libraries;

/// <summary>
/// Handling of whether a Pokemon can evolve
/// </summary>
public interface IEvolutionLibrary
{
    /// <summary>
    /// Check if a Pokemon can evolve if it levels up, Returns true if so.
    /// </summary>
    [PublicAPI]
    bool CanEvolveFromLevelUp(IPokemon pokemon, LevelInt level, [NotNullWhen(true)] out ISpecies? evolveToSpecies,
        [NotNullWhen(true)] out IForm? evolveToForm);
}

/// <inheritdoc />
public class EvolutionLibrary : IEvolutionLibrary
{
    private readonly IMiscLibrary _miscLibrary;
    private readonly IReadOnlySpeciesLibrary _speciesLibrary;

    /// <inheritdoc cref="EvolutionLibrary" />
    public EvolutionLibrary(IMiscLibrary miscLibrary, IReadOnlySpeciesLibrary speciesLibrary)
    {
        _miscLibrary = miscLibrary;
        _speciesLibrary = speciesLibrary;
    }

    /// <inheritdoc />
    public bool CanEvolveFromLevelUp(IPokemon pokemon, LevelInt level,
        [NotNullWhen(true)] out ISpecies? evolveToSpecies, [NotNullWhen(true)] out IForm? evolveToForm)
    {
        foreach (var data in pokemon.Species.EvolutionData)
        {
            switch (data)
            {
                case LevelEvolution lvlEvolution when lvlEvolution.Level <= level:
                case LevelGenderEvolution genderEvolution
                    when genderEvolution.Gender == pokemon.Gender && genderEvolution.Level <= level:
                case HoldItemEvolution holdItemEvolution when pokemon.HasHeldItem(holdItemEvolution.Item):
                case DayHoldItemEvolution dayHoldItemEvolution
                    when _miscLibrary.GetTimeOfDay() is TimeOfDay.Day or TimeOfDay.Morning &&
                         pokemon.HasHeldItem(dayHoldItemEvolution.Item):
                case NightHoldItemEvolution nightHoldItemEvolution
                    when _miscLibrary.GetTimeOfDay() is TimeOfDay.Night or TimeOfDay.Evening &&
                         pokemon.HasHeldItem(nightHoldItemEvolution.Item):
                case HasMoveEvolution hasMoveEvolution when pokemon.HasMove(hasMoveEvolution.MoveName):
                case HappinessEvolution happinessEvolution when pokemon.Happiness >= happinessEvolution.Happiness:
                case HappinessDayEvolution happinessDayEvolution
                    when _miscLibrary.GetTimeOfDay() is TimeOfDay.Day or TimeOfDay.Morning &&
                         pokemon.Happiness >= happinessDayEvolution.Happiness:
                case HappinessNightEvolution happinessNightEvolution
                    when _miscLibrary.GetTimeOfDay() is TimeOfDay.Night or TimeOfDay.Evening &&
                         pokemon.Happiness >= happinessNightEvolution.Happiness:
                {
                    if (_speciesLibrary.TryGet(data.ToSpecies, out var species))
                    {
                        evolveToSpecies = species;
                        evolveToForm = species.GetDefaultForm();
                        return true;
                    }
                    throw new Exception($"Could not find evolution species {data.ToSpecies} for Pokemon {pokemon}");
                }
                case CustomEvolution customEvolution:
                {
                    throw new NotImplementedException(
                        $"Custom evolution for {pokemon.Species} to {customEvolution.ToSpecies} is not implemented");
                }
            }
        }
        evolveToSpecies = null;
        evolveToForm = null;
        return false;
    }
}