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 HiddenPowerTests
{
    public record TestCaseData(IndividualValueStatisticSet Ivs, StringKey ExpectedType, ushort ExpectedPower)
    {
        /// <inheritdoc />
        public override string ToString() =>
            $"Hidden Power type is {ExpectedType}, base power is {ExpectedPower} " +
            $"with IVs: HP {Ivs.Hp}, Atk {Ivs.Attack}, Def {Ivs.Defense}, SpA {Ivs.SpecialAttack}, SpD {Ivs.SpecialDefense}, Spe {Ivs.Speed}";
    }

    public static IEnumerable<Func<TestCaseData>> HiddenPowerTestData()
    {
        yield return () => new TestCaseData(new IndividualValueStatisticSet(31, 31, 31, 31, 31, 31), "dark", 70);
        yield return () => new TestCaseData(new IndividualValueStatisticSet(25, 2, 12, 5, 8, 17), "bug", 31);
        yield return () => new TestCaseData(new IndividualValueStatisticSet(29, 19, 18, 22, 15, 28), "fire", 64);
    }

    [Test, MethodDataSource(nameof(HiddenPowerTestData))]
    public async Task HiddenPower_ChangesType(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");

        var executingMove = Substitute.For<IExecutingMove>();
        var user = Substitute.For<IPokemon>();
        var target = Substitute.For<IPokemon>();
        var dynamicLibrary = Substitute.For<IDynamicLibrary>();
        var staticLibrary = Substitute.For<IStaticLibrary>();

        executingMove.User.Returns(user);
        user.IndividualValues.Returns(test.Ivs);
        user.Library.Returns(dynamicLibrary);
        staticLibrary.Types.Returns(typeLibrary);
        dynamicLibrary.StaticLibrary.Returns(staticLibrary);

        TypeIdentifier? moveType = new TypeIdentifier(1, "normal");

        var hiddenPower = new HiddenPower();
        hiddenPower.ChangeMoveType(executingMove, target, 0, ref moveType);

        await Assert.That(moveType!.Value.Name).IsEqualTo(test.ExpectedType);
    }

    [Test, MethodDataSource(nameof(HiddenPowerTestData))]
    public async Task HiddenPower_ChangesBasePower(TestCaseData test)
    {
        var executingMove = Substitute.For<IExecutingMove>();
        var user = Substitute.For<IPokemon>();
        var target = Substitute.For<IPokemon>();
        var dynamicLibrary = Substitute.For<IDynamicLibrary>();
        var staticLibrary = Substitute.For<IStaticLibrary>();

        executingMove.User.Returns(user);
        user.IndividualValues.Returns(test.Ivs);
        user.Library.Returns(dynamicLibrary);
        dynamicLibrary.StaticLibrary.Returns(staticLibrary);

        var hiddenPower = new HiddenPower();
        ushort power = 0;
        hiddenPower.ChangeBasePower(executingMove, target, 0, ref power);

        await Assert.That(power).IsEqualTo(test.ExpectedPower);
    }
}