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

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

/// <summary>
/// Tests for the <see cref="Copycat"/> move script.
/// Gen VII Bulbapedia behavior: "Copycat causes the user to use the last move that was used in the battle
/// (even if the last move was by the user)." The move fails if no prior move occurred, and certain moves
/// (such as Counter or Copycat itself) cannot be copied.
/// </summary>
public class CopycatTests
{
    private static (Copycat script, IMoveChoice choice) CreateTestSetup(string? lastMoveName)
    {
        var script = new Copycat();
        var user = Substitute.For<IPokemon>();
        var battleData = Substitute.For<IPokemonBattleData>();
        if (lastMoveName == null)
        {
            battleData.LastMoveChoice.Returns((IMoveChoice?)null);
        }
        else
        {
            var moveData = Substitute.For<IMoveData>();
            moveData.Name.Returns(new StringKey(lastMoveName));
            var learnedMove = Substitute.For<ILearnedMove>();
            learnedMove.MoveData.Returns(moveData);
            var lastChoice = Substitute.For<IMoveChoice>();
            lastChoice.ChosenMove.Returns(learnedMove);
            battleData.LastMoveChoice.Returns(lastChoice);
        }
        user.BattleData.Returns(battleData);

        var choice = Substitute.For<IMoveChoice>();
        choice.User.Returns(user);
        return (script, choice);
    }

    /// <summary>
    /// Bulbapedia: "Copycat causes the user to use the last move that was used in the battle".
    /// </summary>
    [Test]
    public async Task ChangeMove_LastMoveIsCopyable_ChangesMoveToLastMove()
    {
        // Arrange
        var (script, choice) = CreateTestSetup("tackle");
        StringKey moveName = "copycat";

        // Act
        script.ChangeMove(choice, ref moveName);

        // Assert
        await Assert.That(moveName).IsEqualTo(new StringKey("tackle"));
        choice.DidNotReceive().Fail();
    }

    /// <summary>
    /// Bulbapedia: "The move fails if no prior move occurred."
    /// </summary>
    [Test]
    public async Task ChangeMove_NoMoveUsedYet_Fails()
    {
        // Arrange
        var (script, choice) = CreateTestSetup(null);
        StringKey moveName = "copycat";

        // Act
        script.ChangeMove(choice, ref moveName);

        // Assert
        choice.Received(1).Fail();
        await Assert.That(moveName).IsEqualTo(new StringKey("copycat"));
    }

    /// <summary>
    /// Bulbapedia lists moves Copycat cannot copy (e.g. Counter, Copycat itself, Protect). If the last
    /// used move is one of those, Copycat fails.
    /// </summary>
    [Test, Arguments("counter"), Arguments("copycat"), Arguments("protect")]
    public async Task ChangeMove_LastMoveNotCopyable_Fails(string lastMove)
    {
        // Arrange
        var (script, choice) = CreateTestSetup(lastMove);
        StringKey moveName = "copycat";

        // Act
        script.ChangeMove(choice, ref moveName);

        // Assert
        choice.Received(1).Fail();
        await Assert.That(moveName).IsEqualTo(new StringKey("copycat"));
    }

    /// <summary>
    /// Technical test: outside of battle (no battle data) there is no last move, so Copycat fails.
    /// </summary>
    [Test]
    public void ChangeMove_NoBattleData_Fails()
    {
        // Arrange
        var script = new Copycat();
        var user = Substitute.For<IPokemon>();
        user.BattleData.Returns((IPokemonBattleData?)null);
        var choice = Substitute.For<IMoveChoice>();
        choice.User.Returns(user);
        StringKey moveName = "copycat";

        // Act
        script.ChangeMove(choice, ref moveName);

        // Assert
        choice.Received(1).Fail();
    }
}