using DbConnectionLibrairie;
using Entities;
using EntityManagers;
using OrderCriterias;
using StubbedDbContextLibrary;

namespace UnitTestsEntityManagers
{
    public class UnitTestAnswerManager : AbstractUnitTestEM
    {

        /// <summary>
        /// test of the 'ajouterAnswer' method of an AnswerManager
        /// </summary>
        [Fact]
        public async void TestAddAnswer()
        {
            using (var context = new MyDbContext(opt))
            {
                await context.Database.EnsureCreatedAsync();
                var manager = new AnswerEntityManager(context);
                var answerToAdd = new AnswerEntity { Id = 1, Content = "chateîgne" };
                var a = await manager.addAnswer(answerToAdd);
                // 1) normal insertion
                // WF : a is the same
                // as answerToAdd
                Assert.Equal(answerToAdd.Content, a.Content);
                Assert.Equal(answerToAdd.Id, a.Id);

                answerToAdd = new AnswerEntity { Id = 5, Content = "damien" };
                a = await manager.addAnswer(answerToAdd);
                // 2) with a random id greater than 0
                // WF : 'a' content is equal to the
                // content of 'answerToAdd'
                // and since it's the second answer
                // that we add, his id equal 2
                Assert.Equal(answerToAdd.Content, a.Content);
                Assert.Equal((uint)2, a.Id);

                answerToAdd = new AnswerEntity { Id = 7, Content = "chateîgne" };
                a = await manager.addAnswer(answerToAdd);
                // 3) with a content that we already have added
                // WF : the function return the answer which already
                // have the same content so the content of 'a' is "châteigne"
                // and his id is 1
                Assert.Equal("chateîgne", a.Content);
                Assert.Equal((uint)1, a.Id);

                answerToAdd = new AnswerEntity { Id = 7, Content = "CHATEÎGNE" };
                a = await manager.addAnswer(answerToAdd);
                // 3) with a content that we already have added
                // but in upperCase instead of lowerCase
                // WF : the function return the answer which
                // already have the same content so the content
                // of 'a' is "chateîgne" and his id is 1
                Assert.Equal("chateîgne", a.Content);
                Assert.Equal((uint)1, a.Id);
            }
        }

        /// <summary>
        /// test of the 'supprimerAnswer' method of an AnswerManager
        /// </summary>
        [Fact]
        public async Task TestRemoveAnswerById()
        {
            using (var context = new StubbedDbContext(opt))
            {
                await context.Database.EnsureCreatedAsync();
                var mgr = new AnswerEntityManager(context);
                // remember that we have only 2 answers in the database :
                // 1) (1, "châteigne")
                // 2) (2, "damien")
                var a = await mgr.removeAnswer(3);
                // 1) with an id greater or equal
                // to the number of element
                // WF : it don't works so 'a' is null,
                Assert.Null(a);

                a = await mgr.removeAnswer(1);
                // 1) with an id that belongs to an answer
                // WF : it works so 'a' is not null,
                // and since we've delete the answer with
                // the id 1, the content is "châteigne"
                Assert.NotNull(a);
                Assert.Equal((uint)1, a.Id);
                Assert.Equal("châteigne", a.Content);

                a = await mgr.removeAnswer(2);
            }
        }

        // /!\ WARNING : since there was 2 answers added to the base,
        // id index while now start at 3 (even though we've delete those)

        /// <summary>
        /// test of the 'getNbElement' method of an AnswerManager
        /// </summary>
        [Fact]
        public async Task TestGetNbElement()
        {
            using (var context = new StubbedDbContext(opt))
            {
                await context.Database.EnsureCreatedAsync();
                var mgr = new AnswerEntityManager(context);
                Assert.Equal(0, mgr.getNbElements()); // just to be sure

                Assert.NotNull(fakeAnswers.datas); // just to be sure

                int count = 0;
                foreach (var answer in fakeAnswers.datas)
                {
                    await mgr.addAnswer(answer);
                    count++;
                    Assert.Equal(count, mgr.getNbElements()); // ok, it's incremented
                }
            }
        }

        /// <summary>
        /// member data for the 'TestGetAnswer' test method
        /// </summary>
        /// <returns>a set of all inline datas for the test method</returns>
        public static IEnumerable<Object?[]> TestGetAnswer_Datas()
        {
            var datas = fakeAnswers.datas;
            uint max = 0;
            foreach (var item in datas)
            {
                yield return new Object[] { item.Id, item };
                if(max < item.Id) max = item.Id;
            }
            yield return new Object[] { max+1, null };
        }
        /// <summary>
        /// test of the 'getAnswer' method of an AnswerManager
        /// </summary>
        /// <param name="id">identifiant of the answer to get</param>
        /// <param name="waiting">answer expected</param>
        /// <returns>nothing but a Task</returns>
        [Theory]
        [MemberData(nameof(TestGetAnswer_Datas))]
        public async Task TestGetAnswer(uint id, AnswerEntity? waiting)
        {
            using (var context = new StubbedDbContext(opt))
            {
                await context.Database.EnsureCreatedAsync();
                var mgr = new AnswerEntityManager(context);

                var tmp = await mgr.getAnswer(id + 2);
                Assert.Equal(waiting?.Content, tmp?.Content);
            }
        }

        /// <summary>
        /// member data for the 'TestGetAnswer' test method
        /// </summary>
        /// <returns>a set of all inline datas for the test method</returns>
        public static IEnumerable<Object?[]> TestGetSomeAnswers_Datas()
        {
            var datas = fakeAnswers.datas;
            var count = datas.Count();
            // remind that to add answers, we haven't ordered this collection
            // but we just have done a foreach

            // 1 : 0 answer from the page 1 then, -1 answer from this page
            // WF : count < 1 so we got null
            yield return new object?[] { 1, 0, null };
            yield return new object?[] { 1, -1, null };

            // 2 : 1 answers from the page 0 then, 1 answers from the page -1
            // WF : page < 1 so we got null
            yield return new object?[] { 0, 1, null };
            yield return new object?[] { -1, 1, null };

            // 3 : 10 answers from the page 1
            // WF : the first 10 element of datas
            yield return new object?[] { 1, 10, datas.Take(10) };

            // 4 : 10 elements from the page 1 order by id
            // WF : the first 10 element of datas order by id
            yield return new object?[] { 1, 10, datas.OrderBy(e => e.Id).Take(10), AnswerOrderCriteria.ById };

            // 5 : 10 elements from the page 1 order by content
            // WF : the first 10 element of datas order by content
            yield return new object?[] { 1, 10, datas.OrderBy(e => e.Content).Take(10), AnswerOrderCriteria.ById };

            // 6 : repeat 3, 4 and 5 with the page 2
            // WF : the 10 next answer from each case
            yield return new object?[] { 2, 10, datas.Skip(10).Take(10) };
            yield return new object?[] { 1, 10, datas.OrderBy(e => e.Id).Skip(10).Take(10), AnswerOrderCriteria.ById };
            yield return new object?[] { 1, 10, datas.OrderBy(e => e.Content).Skip(10).Take(10), AnswerOrderCriteria.ByContent };

            // 7 : count/4 elements from the page 4
            // WF : the lasts count/4 elements of datas
            yield return new object?[] { 4, count / 4, datas.TakeLast(count / 4) };

            // 8 : count/4 elements from the page 5
            // WF : null since num (4) >= count / (count/4)
            yield return new object?[] { 5, count / 4, null };

            // 9 : 10 elements from the page 4
            // WF : since there's only 31 elements in fake datas,
            // we got the last element
            yield return new object?[] { 4, 10, datas.TakeLast(1) };
        }

        /// <summary>
        /// test of the 'getAnswer' method of an AnswerManager
        /// </summary>
        /// <param name="num">the page number</param>
        /// <param name="count">page elements number</param>
        /// <param name="waiting">set of answers expected</param>
        /// <param name="orderCriteria">the order criteria</param>
        /// <returns>nothing but a Task</returns>
        [Theory]
        [MemberData(nameof(TestGetSomeAnswers_Datas))]
        public async Task TestGetSomeAnswers(int num, int count, IEnumerable<AnswerEntity>? waiting, AnswerOrderCriteria orderCriteria = AnswerOrderCriteria.ById)
        {

            using (var context = new StubbedDbContext(opt))
            {
                await context.Database.EnsureCreatedAsync();
                var mgr = new AnswerEntityManager(context);

                var nbPages = count == 0 ? -1 : mgr.getNbElements() / count;
                var tmp = await mgr.getAnswers(num, count, orderCriteria);

                Assert.Equal(nbPages, tmp.nbPages);
                if (waiting == null) Assert.Null(tmp.answers);
                else
                {
                    Assert.NotNull(tmp.answers);
                    if (waiting.Count() == 0) Assert.Empty(tmp.answers);
                    else
                    {
                        for (var i = 0; i < count; i++)
                        {
                            var e1 = waiting.ElementAt(i);
                            var e2 = tmp.answers.ElementAt(i);
                            Assert.Equal(e1.Content, e2.Content);
                            Assert.Equal(e2.IdQuestion, e1.IdQuestion);
                        }
                    }
                }
            }
        }

        // removeAnswer by element

        // updateAnswer
    }
}