using PkmnLib.Dynamic.Libraries;
using PkmnLib.Dynamic.Models;
using PkmnLib.Plugin.Gen7.Scripts.Moves;
using PkmnLib.Static;
using PkmnLib.Static.Libraries;
using PkmnLib.Static.Utils;

namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;

public class MultiAttackTests
{
    public record TestCaseData(string? ItemName, string ExpectedTypeName)
    {
        public override string ToString() => $"MultiAttack {ItemName ?? "None"} => {ExpectedTypeName}";
    }

    public static IEnumerable<Func<TestCaseData>> ChangeMoveTypeData()
    {
        yield return () => new TestCaseData("bug_memory", "bug");
        yield return () => new TestCaseData("dark_memory", "dark");
        yield return () => new TestCaseData("dragon_memory", "dragon");
        yield return () => new TestCaseData("electric_memory", "electric");
        yield return () => new TestCaseData("fairy_memory", "fairy");
        yield return () => new TestCaseData("fighting_memory", "fighting");
        yield return () => new TestCaseData("fire_memory", "fire");
        yield return () => new TestCaseData("flying_memory", "flying");
        yield return () => new TestCaseData("ghost_memory", "ghost");
        yield return () => new TestCaseData("grass_memory", "grass");
        yield return () => new TestCaseData("ground_memory", "ground");
        yield return () => new TestCaseData("ice_memory", "ice");
        yield return () => new TestCaseData("poison_memory", "poison");
        yield return () => new TestCaseData("psychic_memory", "psychic");
        yield return () => new TestCaseData("rock_memory", "rock");
        yield return () => new TestCaseData("steel_memory", "steel");
        yield return () => new TestCaseData("water_memory", "water");
        yield return () => new TestCaseData("something_random", "normal");
        yield return () => new TestCaseData(null, "normal");
    }

    [Test, MethodDataSource(nameof(ChangeMoveTypeData))]
    public async Task ChangeMoveType_UserHoldingMemoryItem_ChangeMoveType(TestCaseData test)
    {
        var typeLibrary = new TypeLibrary();
        typeLibrary.RegisterType("normal");
        typeLibrary.RegisterType("fighting");
        typeLibrary.RegisterType("flying");
        typeLibrary.RegisterType("poison");
        typeLibrary.RegisterType("ground");
        typeLibrary.RegisterType("rock");
        typeLibrary.RegisterType("bug");
        typeLibrary.RegisterType("ghost");
        typeLibrary.RegisterType("steel");
        typeLibrary.RegisterType("fire");
        typeLibrary.RegisterType("water");
        typeLibrary.RegisterType("grass");
        typeLibrary.RegisterType("electric");
        typeLibrary.RegisterType("psychic");
        typeLibrary.RegisterType("ice");
        typeLibrary.RegisterType("dragon");
        typeLibrary.RegisterType("dark");
        typeLibrary.RegisterType("fairy");

        // Arrange
        var move = Substitute.For<IExecutingMove>();
        var target = Substitute.For<IPokemon>();
        var user = Substitute.For<IPokemon>();
        TypeIdentifier? typeIdentifier = new TypeIdentifier(1, "Normal");
        var dynamicLibrary = Substitute.For<IDynamicLibrary>();
        var staticLibrary = Substitute.For<IStaticLibrary>();
        var item = Substitute.For<IItem>();

        user.Library.Returns(dynamicLibrary);
        dynamicLibrary.StaticLibrary.Returns(staticLibrary);
        staticLibrary.Types.Returns(typeLibrary);
        if (test.ItemName != null)
            item.Name.Returns(new StringKey(test.ItemName));
        move.User.Returns(user);

        if (test.ItemName != null)
            user.HeldItem.Returns(item);
        else
            user.HeldItem.Returns((IItem?)null);
        var multiAttack = new MultiAttack();

        // Act
        multiAttack.ChangeMoveType(move, target, 0, ref typeIdentifier);

        // Assert
        await Assert.That(typeIdentifier!.Value.Name.ToString()).IsEqualTo(test.ExpectedTypeName);
    }
}