using PkmnLib.Dynamic.Models;
using PkmnLib.Dynamic.Models.Choices;
using PkmnLib.Static.Moves;
using PkmnLib.Static.Utils;

namespace PkmnLib.Dynamic.AI;

/// <summary>
/// The base class for implementing an AI for Pokémon.
/// </summary>
public abstract class PokemonAI
{
    /// <summary>
    /// The name of the AI.
    /// </summary>
    public StringKey Name { get; set; }

    /// <inheritdoc cref="PokemonAI" />
    protected PokemonAI(StringKey name)
    {
        Name = name;
    }

    /// <summary>
    /// Gets the choice for the Pokémon.
    /// </summary>
    public abstract ITurnChoice GetChoice(IBattle battle, IPokemon pokemon);

    public IEnumerable<(byte side, byte position)> GetValidTargetsForMove(IPokemon user, ILearnedMove move)
    {
        byte GetOppositeSide(byte side) => side == 0 ? (byte)1 : (byte)0;
        var userBattleData = user.BattleData!;
        switch (move.MoveData.Target)
        {
            case MoveTarget.Adjacent:
                yield return (GetOppositeSide(userBattleData.SideIndex), userBattleData.Position);
                break;
            case MoveTarget.AdjacentAlly:
                if (userBattleData.Position > 0)
                    yield return (userBattleData.SideIndex, (byte)(userBattleData.Position - 1));
                if (userBattleData.Battle.PositionsPerSide > userBattleData.Position + 1)
                    yield return (userBattleData.SideIndex, (byte)(userBattleData.Position + 1));
                break;
            case MoveTarget.AdjacentAllySelf:
                if (userBattleData.Position > 0)
                    yield return (userBattleData.SideIndex, (byte)(userBattleData.Position - 1));
                if (userBattleData.Battle.PositionsPerSide > userBattleData.Position + 1)
                    yield return (userBattleData.SideIndex, (byte)(userBattleData.Position + 1));
                yield return (userBattleData.SideIndex, userBattleData.Position);
                break;
            case MoveTarget.AdjacentOpponent:
                yield return (GetOppositeSide(userBattleData.SideIndex), userBattleData.Position);
                if (userBattleData.Position > 0)
                    yield return (GetOppositeSide(userBattleData.SideIndex), (byte)(userBattleData.Position - 1));
                if (userBattleData.Battle.PositionsPerSide > userBattleData.Position + 1)
                    yield return (GetOppositeSide(userBattleData.SideIndex), (byte)(userBattleData.Position + 1));
                break;
            case MoveTarget.All:
                yield return (userBattleData.SideIndex, userBattleData.Position);
                break;
            case MoveTarget.AllAdjacent:
                yield return (userBattleData.SideIndex, userBattleData.Position);
                break;
            case MoveTarget.AllAdjacentOpponent:
                yield return (GetOppositeSide(userBattleData.SideIndex), userBattleData.Position);
                break;
            case MoveTarget.AllAlly:
                yield return (userBattleData.SideIndex, userBattleData.Position);
                break;
            case MoveTarget.AllOpponent:
                yield return (GetOppositeSide(userBattleData.SideIndex), userBattleData.Position);
                break;
            case MoveTarget.Any:
                foreach (var side in userBattleData.Battle.Sides)
                {
                    foreach (var pokemon in side.Pokemon)
                    {
                        if (pokemon?.BattleData == null)
                            continue;
                        yield return (side.Index, pokemon.BattleData!.Position);
                    }
                }
                break;
            case MoveTarget.RandomOpponent:
                yield return (GetOppositeSide(userBattleData.SideIndex), userBattleData.Position);
                break;
            case MoveTarget.SelfUse:
                yield return (userBattleData.SideIndex, userBattleData.Position);
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }
    }
}