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

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

/// <summary>
/// Tests for the <see cref="Conversion"/> move script.
/// Gen VII Bulbapedia behavior: "Conversion now changes the user's current type to match the type of the
/// move the user has in its first move slot (even if that move cannot be selected in battle)."
/// </summary>
public class ConversionTests
{
    private static ILearnedMove CreateLearnedMoveOfType(TypeIdentifier type)
    {
        var moveData = Substitute.For<IMoveData>();
        moveData.MoveType.Returns(type);
        var learned = Substitute.For<ILearnedMove>();
        learned.MoveData.Returns(moveData);
        return learned;
    }

    private static (Conversion script, IExecutingMove move, IPokemon user, IPokemon target, IHitData hitData)
        CreateTestSetup(params ILearnedMove?[] moves)
    {
        var script = new Conversion();
        var move = Substitute.For<IExecutingMove>();
        var user = Substitute.For<IPokemon>();
        user.Moves.Returns(moves);
        move.User.Returns(user);
        var target = Substitute.For<IPokemon>();
        var hitData = Substitute.For<IHitData>();
        move.GetHitData(target, 0).Returns(hitData);
        return (script, move, user, target, hitData);
    }

    /// <summary>
    /// Bulbapedia: "Conversion now changes the user's current type to match the type of the move the user
    /// has in its first move slot".
    /// </summary>
    [Test]
    public void OnSecondaryEffect_UserHasMoves_SetsUserTypeToFirstMoveSlotType()
    {
        // Arrange
        var fireType = new TypeIdentifier(3, new StringKey("fire"));
        var waterType = new TypeIdentifier(4, new StringKey("water"));
        var (script, move, user, target, _) =
            CreateTestSetup(CreateLearnedMoveOfType(fireType), CreateLearnedMoveOfType(waterType));

        // Act
        script.OnSecondaryEffect(move, target, 0);

        // Assert - the first slot's type (fire) is used, not the second
        user.Received(1).SetTypes(Arg.Is<IReadOnlyList<TypeIdentifier>>(t => t.Count == 1 && t[0] == fireType));
    }

    /// <summary>
    /// Bulbapedia: the type comes from "the move the user has in its first move slot" — with no known
    /// moves there is no type to copy and the move fails.
    /// </summary>
    [Test]
    public void OnSecondaryEffect_UserHasNoMoves_Fails()
    {
        // Arrange
        var (script, move, user, target, hitData) = CreateTestSetup(null, null, null, null);

        // Act
        script.OnSecondaryEffect(move, target, 0);

        // Assert
        hitData.Received(1).Fail();
        user.DidNotReceive().SetTypes(Arg.Any<IReadOnlyList<TypeIdentifier>>());
    }
}