using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

using StackExchange.Redis;
using Xunit;
using Xunit.Extensions;
using Xunit.Serialization;
using Xunit.Abstractions;
using Xunit.Sdk;

using RedisStudyModel;
using RedisStuy;

namespace RedisStudyTest
{
    [Trait("RedisString", "All")]
    public class RedisListStudyTest : IDisposable
    {
        #region 初始化
        private readonly ITestOutputHelper testOutput;
        private IDatabase redisDatabase = null;
        private RedisListStudy redisListStudy = null;
        private TimeSpan defaultExpiry = TimeSpan.FromSeconds(20);
        private string defaultRedisKey = "RedisStudy:List:xUnitTest";

        /// <summary>
        /// 构造
        /// </summary>
        public RedisListStudyTest(ITestOutputHelper output)
        {
            this.testOutput = output;
            redisDatabase = RedisHelper.GetRedisDatabase();
            redisListStudy = new RedisListStudy();

        }
        #endregion

        #region ListLeftPush
        [Fact]
        public void ListLeftPushTest()
        {
            var listLength = redisListStudy.ListLeftPush(defaultRedisKey, "first");
            Assert.Equal(1, listLength);

            listLength = redisListStudy.ListLeftPush(defaultRedisKey, "second");
            Assert.Equal(2, listLength);

            var second = redisListStudy.ListLeftPop(defaultRedisKey);
            Assert.Equal("second", second);

            var first = redisListStudy.ListLeftPop(defaultRedisKey);
            Assert.Equal("first", first);
        }

        [Fact]
        public void ListLeftPushsTest()
        {
            RedisValue[] listValues = new RedisValue[]
            {
                "first",
                "secnd",
                "third"
            };

            var listLenth = redisListStudy.ListLeftPush(defaultRedisKey, listValues);
            Assert.Equal(3, listLenth);

            Assert.Equal(listValues[2], redisListStudy.ListGetByIndex(defaultRedisKey, 0));
            Assert.Equal(listValues[1], redisListStudy.ListGetByIndex(defaultRedisKey, 1));
            Assert.Equal(listValues[0], redisListStudy.ListGetByIndex(defaultRedisKey, 2));
        }
        #endregion

        #region ListRightPush
        [Fact]
        public void ListRightPushTest()
        {
            var listLength = redisListStudy.ListRightPush(defaultRedisKey, "first");
            Assert.Equal(1, listLength);

            listLength = redisListStudy.ListRightPush(defaultRedisKey, "second");
            Assert.Equal(2, listLength);

            var second = redisListStudy.ListRightPop(defaultRedisKey);
            Assert.Equal("second", second);

            var first = redisListStudy.ListRightPop(defaultRedisKey);
            Assert.Equal("first", first);
        }

        [Fact]
        public void ListRightPushsTest()
        {
            RedisValue[] listValues = new RedisValue[]
            {
                "first",
                "secnd",
                "third"
            };
            var listLenth = redisListStudy.ListRightPush(defaultRedisKey, listValues);
            Assert.Equal(3, listLenth);
        }

        #endregion

        #region ListInsertBefore
        /// <summary>
        /// Key不存在时,不执行操作,返回0
        /// </summary>
        [Fact]
        public void ListInsertBeforeNotKeyTest()
        {
            var listLenth = redisListStudy.ListInsertBefore(defaultRedisKey, "first", "firstBefore");
            Assert.Equal(0, listLenth);
        }

        /// <summary>
        /// 未找到指定参照值时,返回 -1
        /// </summary>
        [Fact]
        public void ListInsertBeforeNotPivotTest()
        {
            redisListStudy.ListLeftPush(defaultRedisKey, "second");

            var listLenth = redisListStudy.ListInsertBefore(defaultRedisKey, "first", "firstBefore");
            Assert.Equal(-1, listLenth);
        }

        [Fact]
        public void ListInsertBeforeTest()
        {
            redisListStudy.ListLeftPush(defaultRedisKey, "third");

            var listLenth = redisListStudy.ListInsertBefore(defaultRedisKey, "third", "second");
            Assert.Equal(2, listLenth);

            listLenth = redisListStudy.ListInsertBefore(defaultRedisKey, "second", "first");
            Assert.Equal(3, listLenth);
        }
        #endregion

        #region ListInsertAfter
        /// <summary>
        /// Key不存在时,不执行操作,返回0
        /// </summary>
        [Fact]
        public void ListInsertAfterNotKeyTest()
        {
            var listLenth = redisListStudy.ListInsertAfter(defaultRedisKey, "first", "firstAfter");
            Assert.Equal(0, listLenth);
        }

        /// <summary>
        /// 未找到指定参照值时,返回 -1
        /// </summary>
        [Fact]
        public void ListInsertAfterNotPivotTest()
        {
            redisListStudy.ListLeftPush(defaultRedisKey, "second");

            var listLenth = redisListStudy.ListInsertAfter(defaultRedisKey, "first", "firstAfter");
            Assert.Equal(-1, listLenth);
        }

        [Fact]
        public void ListInsertAfterTest()
        {
            redisListStudy.ListLeftPush(defaultRedisKey, "first");

            var listLenth = redisListStudy.ListInsertAfter(defaultRedisKey, "first", "second");
            Assert.Equal(2, listLenth);

            listLenth = redisListStudy.ListInsertAfter(defaultRedisKey, "second", "third");
            Assert.Equal(3, listLenth);
        }
        #endregion

        #region ListSetByIndex
        [Fact]
        public void ListSetByIndexNotKeyTest()
        {
            Assert.Throws<RedisServerException>(() => redisListStudy.ListSetByIndex(defaultRedisKey, 0, "first"));
        }

        [Fact]
        public void ListSetByIndexTest()
        {
            redisListStudy.ListLeftPush(defaultRedisKey, "first");

            redisListStudy.ListSetByIndex(defaultRedisKey, 0, "firstSetByIndex");

            var first = redisListStudy.ListLeftPop(defaultRedisKey);

            Assert.Equal("firstSetByIndex", first);
        }
        #endregion

        #region ListGetByIndex

        [Fact]
        public void ListGetByIndexNotKeyTest()
        {
            var result = redisListStudy.ListGetByIndex(defaultRedisKey, 0);
            Assert.False(result.HasValue);
        }

        [Fact]
        public void Test()
        {
            redisListStudy.ListLeftPush(defaultRedisKey, new RedisValue[] { "first", "second", "third" });

            var result = redisListStudy.ListGetByIndex(defaultRedisKey, 0);
            Assert.Equal("third", result);

            result = redisListStudy.ListGetByIndex(defaultRedisKey, 1);
            Assert.Equal("second", result);

            result = redisListStudy.ListGetByIndex(defaultRedisKey, 2);
            Assert.Equal("first", result);
        }
        #endregion

        #region ListLength
        [Fact]
        public void ListLengthNotKeyTest()
        {
            var listLenth = redisListStudy.ListLength(defaultRedisKey);
            Assert.Equal(0, listLenth);
        }

        [Fact]
        public void ListLengthTest()
        {
            redisListStudy.ListLeftPush(defaultRedisKey, "first");
            var listLenth = redisListStudy.ListLength(defaultRedisKey);
            Assert.Equal(1, listLenth);

            redisListStudy.ListLeftPush(defaultRedisKey, "second");
            listLenth = redisListStudy.ListLength(defaultRedisKey);
            Assert.Equal(2, listLenth);

            redisListStudy.ListLeftPush(defaultRedisKey, "third");
            listLenth = redisListStudy.ListLength(defaultRedisKey);
            Assert.Equal(3, listLenth);
        }
        #endregion

        #region  ListRange

        [Fact]
        public void ListRangeNotKeyTest()
        {
            RedisValue[] values = redisListStudy.ListRange(defaultRedisKey, 0, -1);
            Assert.Empty(values);
        }

        [Fact]
        public void ListRangeTest()
        {
            redisListStudy.ListLeftPush(defaultRedisKey, "first");
            var values = redisListStudy.ListRange(defaultRedisKey, 0, -1);
            Assert.NotEmpty(values);
            Assert.Contains("first", values);

            redisListStudy.ListLeftPush(defaultRedisKey, "second");
            values = redisListStudy.ListRange(defaultRedisKey, 0, -1);
            Assert.NotEmpty(values);
            Assert.Contains("first", values);
            Assert.Contains("second", values);
        }

        #endregion

        #region ListRemove
        [Fact]
        public void ListRemoveNotKeyTest()
        {
            var removeNum = redisListStudy.ListRemove(defaultRedisKey, "first");
            Assert.Equal(0, removeNum);
        }

        [Fact]
        public void ListRemoveTest()
        {
            var removeNum = 0L;

            redisListStudy.ListLeftPush(defaultRedisKey, new RedisValue[] { "first", "second", "third", "four" });
            removeNum = redisListStudy.ListRemove(defaultRedisKey, "first", 0);
            Assert.Equal(1, removeNum);

            redisListStudy.ListLeftPush(defaultRedisKey, new RedisValue[] { "third", "four" });
            removeNum = redisListStudy.ListRemove(defaultRedisKey, "third", 0);
            Assert.Equal(2, removeNum);

            redisListStudy.ListLeftPush(defaultRedisKey, new RedisValue[] { "1", "2", "1", "2", "1", "2" });
            removeNum = redisListStudy.ListRemove(defaultRedisKey, "2", 0);
            Assert.Equal(3, removeNum);
        }
        #endregion

        #region ListTrim

        [Fact]
        public void ListTrimNotKeyTest()
        {
            redisListStudy.ListTrim(defaultRedisKey, 2, 5);
            var listCount = redisListStudy.ListLength(defaultRedisKey);
            Assert.Equal(0, listCount);
        }

        [Fact]
        public void ListTrimTest()
        {
            RedisValue[] values = new RedisValue[] 
            {
                "first",
                "second",
                "third",
                "four"
            };
            redisListStudy.ListLeftPush(defaultRedisKey, values);

            redisListStudy.ListTrim(defaultRedisKey,1,-1);
            Assert.Equal(3, redisListStudy.ListLength(defaultRedisKey));

            redisListStudy.ListTrim(defaultRedisKey, 2, -1);
            Assert.Equal(1, redisListStudy.ListLength(defaultRedisKey));
        }
        #endregion

        #region ListRightPopLeftPush

        [Fact]
        public void ListRightPopLeftPushNotKeyTest()
        {
            //操作
            var firsValue = redisListStudy.ListRightPopLeftPush(defaultRedisKey, "RedisStudy:List:xUnitTest2");

            //断言
            Assert.False(firsValue.HasValue);
            Assert.False(redisDatabase.KeyExists("RedisStudy:List:xUnitTest2"));

            //清理
            redisDatabase.KeyDelete("RedisStudy:List:xUnitTest");
        }

        /// <summary>
        /// 目标list不存在
        /// </summary>
        [Fact]
        public void ListRightPopLeftPushNotDestinationTest()
        {
            //准备
            redisListStudy.ListLeftPush(defaultRedisKey, new RedisValue[] { "first", "second" });

            //操作
            var firsValue = redisListStudy.ListRightPopLeftPush(defaultRedisKey, "RedisStudy:List:xUnitTest2");

            //断言
            Assert.Equal(1, redisListStudy.ListLength(defaultRedisKey));
            Assert.Equal("first",firsValue);
            Assert.True(redisDatabase.KeyExists("RedisStudy:List:xUnitTest2"));

            //清理
            redisDatabase.KeyDelete("RedisStudy:List:xUnitTest");
        }

        [Fact]
        public void ListRightPopLeftPushTest()
        {
            //准备
            string secondListKey = "RedisStudy:List:xUnitTest2";
            redisListStudy.ListLeftPush(defaultRedisKey, new RedisValue[] { "first", "second" });

            redisListStudy.ListLeftPush(secondListKey, new RedisValue[] {});

            //操作
            var resultValue = redisListStudy.ListRightPopLeftPush(defaultRedisKey, secondListKey);

            //断言
            Assert.Equal("first", resultValue);
            Assert.Equal(1, redisListStudy.ListLength(defaultRedisKey));
            Assert.True(redisDatabase.KeyExists(secondListKey));

            //清理
            redisDatabase.KeyDelete(secondListKey);
        }
        #endregion

        #region 清理
        public void Dispose()
        {
            redisDatabase.KeyDelete(defaultRedisKey);
        }
        #endregion
    }
}