using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using CSPath;
using PkmnLib.Dynamic.Models;
using PkmnLib.Dynamic.Models.Choices;
using JsonSerializer = System.Text.Json.JsonSerializer;

namespace PkmnLib.Tests.Integration.Models;


[JsonDerivedType(typeof(SetPokemonAction), "setPokemon")]
[JsonDerivedType(typeof(SetMoveChoiceAction), "setMoveChoice")]
[JsonDerivedType(typeof(SetPassChoiceAction), "setPassChoice")]
[JsonDerivedType(typeof(AssertAction), "assert")]
public abstract class IntegrationTestAction
{
    public abstract Task Execute(IBattle battle);
}

public class SetPokemonAction : IntegrationTestAction
{
    public List<byte> Place { get; set; } = null!;
    public List<byte> FromParty { get; set; } = null!;

    public override Task Execute(IBattle battle)
    {
        var mon = battle.Parties[FromParty[0]].Party[FromParty[1]];
        battle.Sides[Place[0]].SwapPokemon(Place[1], mon);
        return Task.CompletedTask;
    }
}

public class SetMoveChoiceAction : IntegrationTestAction
{
    public List<byte> Place { get; set; } = null!;
    public string Move { get; set; } = null!;
    public List<byte> Target { get; set; } = null!;


    /// <inheritdoc />
    public override async Task Execute(IBattle battle)
    {
        var user = battle.Sides[Place[0]].Pokemon[Place[1]];
        await Assert.That(user).IsNotNull();
        var move = user.Moves.First(m => m?.MoveData.Name == Move);
        await Assert.That(move).IsNotNull();
        var res = battle.TrySetChoice(new MoveChoice(user, move, Target[0], Target[1]));
        await Assert.That(res).IsTrue();
    }
}

public class SetPassChoiceAction : IntegrationTestAction
{
    public List<byte> Place { get; set; } = null!;

    /// <inheritdoc />
    public override async Task Execute(IBattle battle)
    {
        var user = battle.Sides[Place[0]].Pokemon[Place[1]];
        await Assert.That(user).IsNotNull();
        var res = battle.TrySetChoice(new PassChoice(user));
        await Assert.That(res).IsTrue();
    }
}

public class AssertAction : IntegrationTestAction
{
    public string Value { get; set; } = null!;
    public JsonNode Expected { get; set; } = null!;

    /// <inheritdoc />
    public override async Task Execute(IBattle battle)
    {
        var list = battle.Path(Value).ToList();
        var value = list.Count == 1 ? list[0] : list;
        
        var serialized = JsonSerializer.Serialize(value);
        await Assert.That(serialized).IsEqualTo(Expected.ToJsonString());
    }
}