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

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

/// <summary>
/// Tests for the <see cref="Brine"/> move script.
/// Behavior is verified against the Bulbapedia page for Brine.
/// </summary>
public class BrineTests
{
    /// <summary>
    /// Creates a fully mocked test setup for Brine tests, with a target whose max HP
    /// (<see cref="IPokemon.BoostedStats"/>) and <see cref="IPokemon.CurrentHealth"/> are configured.
    /// </summary>
    private static (Brine brine, IExecutingMove move, IPokemon target) CreateTestSetup(uint maxHp, uint currentHp)
    {
        var brine = new Brine();
        var move = Substitute.For<IExecutingMove>();
        var target = Substitute.For<IPokemon>();
        target.BoostedStats.Returns(new StatisticSet<uint>(maxHp, 0, 0, 0, 0, 0));
        target.CurrentHealth.Returns(currentHp);
        return (brine, move, target);
    }

    /// <summary>
    /// Bulbapedia: "Brine inflicts damage, and its power doubles to 130 when the target is at or below
    /// 50% health."
    /// At exactly half HP, the base power of 65 doubles to 130.
    /// </summary>
    [Test]
    public async Task ChangeBasePower_TargetAtExactlyHalfHp_BasePowerDoubles()
    {
        // Arrange
        var (brine, move, target) = CreateTestSetup(100, 50);
        ushort basePower = 65;

        // Act
        brine.ChangeBasePower(move, target, 0, ref basePower);

        // Assert
        await Assert.That(basePower).IsEqualTo((ushort)130);
    }

    /// <summary>
    /// Bulbapedia: "its power doubles to 130 when the target is at or below 50% health."
    /// Tests several HP fractions at or below half, including odd max HP values where 50% is not
    /// a whole number (50/101 ≈ 49.5% is below half).
    /// </summary>
    [Test, Arguments(100u, 49u), Arguments(100u, 1u), Arguments(200u, 100u), Arguments(101u, 50u)]
    public async Task ChangeBasePower_TargetAtOrBelowHalfHp_BasePowerDoubles(uint maxHp, uint currentHp)
    {
        // Arrange
        var (brine, move, target) = CreateTestSetup(maxHp, currentHp);
        ushort basePower = 65;

        // Act
        brine.ChangeBasePower(move, target, 0, ref basePower);

        // Assert
        await Assert.That(basePower).IsEqualTo((ushort)130);
    }

    /// <summary>
    /// Bulbapedia: "its power doubles to 130 when the target is at or below 50% health."
    /// Above 50% health the power must remain unchanged, including one HP above half and odd max HP
    /// values where 51/101 ≈ 50.5% is just above half.
    /// </summary>
    [Test, Arguments(100u, 51u), Arguments(100u, 100u), Arguments(101u, 51u), Arguments(200u, 101u)]
    public async Task ChangeBasePower_TargetAboveHalfHp_BasePowerUnchanged(uint maxHp, uint currentHp)
    {
        // Arrange
        var (brine, move, target) = CreateTestSetup(maxHp, currentHp);
        ushort basePower = 65;

        // Act
        brine.ChangeBasePower(move, target, 0, ref basePower);

        // Assert
        await Assert.That(basePower).IsEqualTo((ushort)65);
    }

    /// <summary>
    /// Technical test: doubling an already very high base power must clamp to
    /// <see cref="ushort.MaxValue"/> instead of overflowing.
    /// </summary>
    [Test]
    public async Task ChangeBasePower_DoublingWouldOverflow_ClampsToMax()
    {
        // Arrange
        var (brine, move, target) = CreateTestSetup(100, 50);
        ushort basePower = ushort.MaxValue - 100;

        // Act
        brine.ChangeBasePower(move, target, 0, ref basePower);

        // Assert
        await Assert.That(basePower).IsEqualTo(ushort.MaxValue);
    }
}