using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using PkmnLibSharp.Battling;
using PkmnLibSharp.Battling.ChoiceTurn;
using PkmnLibSharp.Battling.Events;

namespace PkmnLibSharpTests.Battling.BattleTests
{
    public class BasicBattleTests
    {
        [Test]
        public void InitializeBattle()
        {
            var lib = BattleLibraryHelper.GetLibrary();
            var battle = new BattleBuilder(lib, true, 2, 1).Build();
            Assert.AreEqual(lib, battle.Library);
            Assert.AreEqual(true, battle.CanFlee);
            Assert.AreEqual(2, battle.SidesCount);
            Assert.AreEqual(false, battle.HasEnded);
            Assert.AreEqual(0, battle.PartiesCount);
            battle.Dispose();
        }

        private static PokemonParty BuildTestParty(BattleLibrary lib)
        {
            var party = new PokemonParty();
            party.SwapInto(0, 
                new PokemonBuilder(lib, "testSpecies", 50)
                    .LearnMove("testMove", MoveLearnMethod.Unknown)
                    .Build());
            return party;
        }

        [Test]
        public void InitializeBattleWithParties()
        {
            var lib = BattleLibraryHelper.GetLibrary();
            var p1 = BuildTestParty(lib);
            var p2 = BuildTestParty(lib);
            var battle = 
                new BattleBuilder(lib, true, 2, 1)
                    .WithPartyOnPositions(p1, new BattlePosition(0, 0))
                    .WithPartyOnPositions(p2, new BattlePosition(1, 0))
                    .Build();
            Assert.AreEqual(2, battle.PartiesCount);
            Assert.AreEqual(p1, battle.Parties[0].Party);
            Assert.AreEqual(p2, battle.Parties[1].Party);
            battle.Dispose();
        }
        
        [Test]
        public void RunTurn()
        {
            var lib = BattleLibraryHelper.GetLibrary();
            var p1 = BuildTestParty(lib);
            var p2 = BuildTestParty(lib);
            var battle = 
                new BattleBuilder(lib, true, 2, 1)
                    .WithPartyOnPositions(p1, new BattlePosition(0, 0))
                    .WithPartyOnPositions(p2, new BattlePosition(1, 0))
                    .Build();
            
            battle.SwitchPokemon(0, 0, p1.GetAtIndex(0));
            battle.SwitchPokemon(1, 0, p2.GetAtIndex(0));
            Assert.AreEqual(0, battle.CurrentTurn);

            var moveTurn1 = new MoveTurnChoice(p1.GetAtIndex(0), p1.GetAtIndex(0).Moves[0], 1, 0 );
            var moveTurn2 = new MoveTurnChoice(p2.GetAtIndex(0), p2.GetAtIndex(0).Moves[0], 0, 0 );

            Assert.That(battle.TrySetChoice(moveTurn1));
            Assert.That(battle.TrySetChoice(moveTurn2));
            
            Assert.AreEqual(battle.CurrentTurn, 1);
            
            battle.Dispose();
        }

        
        [Test]
        public void EventListener()
        {
            var lib = BattleLibraryHelper.GetLibrary();
            var p1 = BuildTestParty(lib);
            var p2 = BuildTestParty(lib);
            var battle = 
                new BattleBuilder(lib, true, 2, 1)
                    .WithPartyOnPositions(p1, new BattlePosition(0, 0))
                    .WithPartyOnPositions(p2, new BattlePosition(1, 0))
                    .WithRandomSeed(20)
                    .Build();
            
            var evts = new List<EventData>();
            var eventListener = new BattleEventListener(evt =>
            {
                evts.Add(evt);
                return Task.CompletedTask;
            });
            battle.RegisterEventListener(eventListener);
            
            battle.SwitchPokemon(0, 0, p1.GetAtIndex(0));
            battle.SwitchPokemon(1, 0, p2.GetAtIndex(0));
            Assert.AreEqual(0, battle.CurrentTurn);

            var moveTurn1 = new MoveTurnChoice(p1.GetAtIndex(0), p1.GetAtIndex(0).Moves[0], 1, 0 );
            var moveTurn2 = new MoveTurnChoice(p2.GetAtIndex(0), p2.GetAtIndex(0).Moves[0], 0, 0 );

            Assert.That(battle.TrySetChoice(moveTurn1));
            Assert.That(battle.TrySetChoice(moveTurn2));
            Assert.AreEqual(1, battle.CurrentTurn);
            
            eventListener.EnsureFinishedListening();

            var damageEvents = evts.Where(x => x.Kind == EventDataKind.Damage).Cast<DamageEvent>().ToArray();
            Assert.AreEqual(2, damageEvents.Length);
            Assert.AreEqual(damageEvents[0].Pokemon, p2.GetAtIndex(0));
            Assert.AreEqual(damageEvents[1].Pokemon, p1.GetAtIndex(0));
            
            battle.Dispose();
        }

    }
}