using CoreLibrary;
using CoreLibrary.Exceptions;
using CoreLibrary.Regles;
using CoreLibrary.Joueurs;
using CoreLibrary.Core;
using System.Reflection;
using Xunit;

namespace UnitTesting
{
    public class ReglesClassiquesUT
    {
        [Fact]
        public void TestNom()
        {
            ReglesClassiques regle = new ReglesClassiques();
            Assert.Equal("Règles classiques", regle.Nom);
        }
        [Fact]
        public void TestConstructeur()
        {
            ReglesClassiques regles = new ReglesClassiques();

            Assert.NotNull(regles);
            Assert.Equal(0, regles.NbJoueurs);
            Assert.Equal(2, regles.NbJoueursMaximum);
        }

        [Fact]
        public void TestAjouterJoueur() 
        {
            ReglesClassiques regles = new ReglesClassiques();

            regles.AjouterJoueur("MonJoueur");

            Assert.Equal(1, regles.NbJoueurs);
        }

        [Fact]
        public void TestJoueurCourantAvecJoueur()
        {
            ReglesClassiques regles = new ReglesClassiques();
            regles.AjouterJoueur("joueur1");
            regles.AjouterJoueur("joueur2");
            regles.CommencerLaPartie();

            Joueur joueurCourant = regles.JoueurCourant();

            Assert.NotNull(joueurCourant);
            Assert.Equal("joueur1", joueurCourant.Nom);
        }

        [Fact]
        public void TestJoueurCourantSansJoueur()
        {
            ReglesClassiques regles = new ReglesClassiques();
            Assert.Throws<PartieNonCommenceeException>(() => regles.JoueurCourant());
        }

        [Fact]
        public void TestPasserLaMainValide()
        {
            ReglesClassiques regles = new ReglesClassiques();
            regles.AjouterJoueur("joueur1");
            regles.AjouterJoueur("joueur2");

            regles.CommencerLaPartie();
            Joueur joueurCourantInitial = regles.JoueurCourant();
            regles.PasserLaMain();
            Joueur joueurCourantSuivant = regles.JoueurCourant();

            Assert.NotEqual(joueurCourantInitial, joueurCourantSuivant);
            Assert.Equal("joueur2", joueurCourantSuivant.Nom);
        }

        [Fact]
        public void TestPasserLaMainCompteurReinitialise()
        {
            ReglesClassiques regles = new ReglesClassiques();
            Type type = typeof(ReglesClassiques);

            regles.AjouterJoueur("joueur1");
            regles.AjouterJoueur("joueur2");
            regles.CommencerLaPartie();

            FieldInfo? fieldInfo = type.GetField("joueurCourant", BindingFlags.NonPublic | BindingFlags.Instance);
            Assert.NotNull(fieldInfo);
            int? joueurCourant = (int?)fieldInfo.GetValue(regles);

            regles.PasserLaMain();
            regles.PasserLaMain();
            regles.PasserLaMain();

            Assert.NotNull(joueurCourant);
            Assert.Equal(0, joueurCourant);
        }

        [Fact]
        public void TestPasserLaMainInvalide()
        {
            ReglesClassiques regles = new ReglesClassiques();
            Assert.Throws<PartieNonCommenceeException>(() => regles.PasserLaMain());
        }

        [Fact]
        public void TestGenererCode()
        {
            ReglesClassiques regles = new ReglesClassiques();
            Code code = regles.GenererCode();

            Assert.NotNull(code);
            Assert.Equal(regles.TailleCodeMaximum, code.TailleMaximale());
        }

        [Fact]
        public void TestCommencerLaPartie()
        {
            ReglesClassiques regles = new ReglesClassiques();
            regles.AjouterJoueur("joueur1");
            regles.AjouterJoueur("joueur2");

            regles.CommencerLaPartie();
            Joueur joueurCourant = regles.JoueurCourant();

            Assert.NotNull(joueurCourant);
            Assert.Equal("joueur1", joueurCourant.Nom);
        }

        [Fact]
        public void TestEstTermineeFalseUnJoueur()
        {
            ReglesClassiques regles = new ReglesClassiques();
            regles.AjouterJoueur("joueur1");
            regles.CommencerLaPartie();
            regles.PasserLaMain();

            bool result = regles.EstTerminee();

            Assert.False(result);
        }

        [Fact]
        public void TestEstTermineeFalse()
        {
            ReglesClassiques regles = new ReglesClassiques();
            regles.AjouterJoueur("joueur1");
            regles.AjouterJoueur("joueur2");
            regles.CommencerLaPartie();

            bool estTerminee = regles.EstTerminee();

            Assert.False(estTerminee);
        }

        [Fact]
        public void TestEstTermineeVictoire()
        {
            ReglesClassiques regles = new ReglesClassiques();
            Partie partie = new Partie(regles);
            Joueur joueur1 = regles.AjouterJoueur("joueur1");
            Joueur joueur2 = regles.AjouterJoueur("joueur2");

            regles.CommencerLaPartie();
            Plateau plateauj1 = regles.JoueurCourant().Plateau;
            Type type = typeof(Plateau);
            FieldInfo? fieldInfo = type.GetField("codeSecret", BindingFlags.NonPublic | BindingFlags.Instance);
            Assert.NotNull(fieldInfo);
            Code? codeSecret = (Code?)fieldInfo.GetValue(plateauj1);
            Assert.NotNull(codeSecret);
            regles.JoueurCourant().Plateau.AjouterCode(codeSecret);
            bool estTerminee = regles.EstTerminee();
            Assert.True(estTerminee);
        }

        [Fact]
        public void TestEstTermineeToursMaximesAtteints()
        {
            ReglesClassiques regles = new ReglesClassiques();
            Partie partie = new Partie(regles);
            regles.AjouterJoueur("joueur1");
            regles.AjouterJoueur("joueur2");
            regles.CommencerLaPartie();
            Code code = new Code([new Jeton(Couleur.ROUGE), new Jeton(Couleur.BLEU), new Jeton(Couleur.BLANC), new Jeton(Couleur.JAUNE)]);
            for (int i = 1; i <= regles.TourMaximum*regles.NbJoueursMaximum; i++)
            {
                regles.JoueurCourant().Plateau.AjouterCode(code);
                regles.PasserLaMain();
                
            }
            bool estTerminee = regles.EstTerminee();
            Assert.True(estTerminee);
        }



        [Fact]
        public void TestGagnantsAucunGagnants()
        {
            ReglesClassiques regles = new ReglesClassiques();
            regles.AjouterJoueur("joueur1");
            regles.AjouterJoueur("joueur2");
            regles.CommencerLaPartie();

            IEnumerable<Joueur> gagnants = regles.Gagnants();

            Assert.Empty(gagnants);
        }

        [Fact]
        public void TestGagnantsGagnants()
        {

            ReglesClassiques regles = new ReglesClassiques();
            Partie partie = new Partie(regles);

            regles.AjouterJoueur("joueur1");
            regles.AjouterJoueur("joueur2");
            
            regles.CommencerLaPartie();
            Plateau plateauj1 = regles.JoueurCourant().Plateau;
            Type type = typeof(Plateau);
            FieldInfo? fieldInfo = type.GetField("codeSecret", BindingFlags.NonPublic | BindingFlags.Instance);
            Assert.NotNull(fieldInfo);
            Code? codeSecret = (Code?)fieldInfo.GetValue(plateauj1);
            Assert.NotNull(codeSecret);
            regles.JoueurCourant().Plateau.AjouterCode(codeSecret);
            IEnumerable<Joueur> gagnants = regles.Gagnants();
            Assert.Single(gagnants);
            Assert.Contains(regles.JoueurCourant(), gagnants);

        }

    }
}
