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

using StackExchange.Redis;

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

using RedisStuy;

namespace RedisStudyTest
{
    /// <summary>
    /// Redis 集合学习 测试
    /// </summary>
    [Trait("RedisSet", "All")]
    public class RedisSetStudyTest:IDisposable
    {
        #region 初始化
        private readonly ITestOutputHelper testOutput;
        private IDatabase redisDatabase = null;
        private RedisSetStudy redisSetStudy = null;
        private TimeSpan defaultExpiry = TimeSpan.FromSeconds(20);
        private string defaultRedisKey = "RedisStudy:Set:xUnitTest";

        /// <summary>
        /// 构造
        /// </summary>
        public RedisSetStudyTest(ITestOutputHelper output)
        {
            this.testOutput = output;
            redisDatabase = RedisHelper.GetRedisDatabase();
            redisSetStudy = new RedisSetStudy();
        }
        #endregion

        #region SetAdd
        [Fact]
        public void SetAdd_Exception_Test()
        {
            Assert.Throws<RedisServerException>(() => redisSetStudy.SetAdd(defaultRedisKey, new RedisValue[] { }));
        }

        [Fact]
        public void SetAddOneTest()
        {
            var addResult = redisSetStudy.SetAdd(defaultRedisKey, "first");
            Assert.True(addResult);

            addResult = redisSetStudy.SetAdd(defaultRedisKey, "second");
            Assert.True(addResult);

            var setMembers = redisSetStudy.SetMembers(defaultRedisKey);

            Assert.NotEmpty(setMembers);
            Assert.Equal(2, setMembers.Length);
            Assert.Contains("first", setMembers);
            Assert.Contains("second", setMembers);
        }

        [Fact]
        public void SetAddGroupTest()
        {
            RedisValue[] values = new RedisValue[]
            {
                "first",
                "second",
                "third",
                "four",
            };

            var setAddNumber = redisSetStudy.SetAdd(defaultRedisKey, values);
            Assert.Equal(4, setAddNumber);

            //忽略已存在的项
            setAddNumber = redisSetStudy.SetAdd(defaultRedisKey, values);
            Assert.Equal(0, setAddNumber);

            var setMember = redisSetStudy.SetMembers(defaultRedisKey);
            Assert.Equal(4, setMember.Length);

            Assert.Contains(values[0], setMember);
            Assert.Contains(values[1], setMember);
            Assert.Contains(values[2], setMember);
            Assert.Contains(values[3], setMember);
        }
        #endregion

        #region SetContains

        [Fact]
        public void SetContainsNotKeyTest()
        {
            var extis = redisSetStudy.SetContains(defaultRedisKey, "first");
            Assert.False(extis);
        }

        [Fact]
        public void SetContainsTest()
        {
            RedisValue[] values = new RedisValue[] 
            {
                "first",
                "second",
                "third",
                "four",
            };
            redisSetStudy.SetAdd(defaultRedisKey, values);

            var memberExist = redisSetStudy.SetContains(defaultRedisKey, "first");
            Assert.True(memberExist);

            memberExist = redisSetStudy.SetContains(defaultRedisKey, "FIRST");
            Assert.False(memberExist);

            memberExist = redisSetStudy.SetContains(defaultRedisKey, "365Day");
            Assert.False(memberExist);
        }
        #endregion

        #region SetLength

        [Fact]
        public void SetLengthNotKeyTest()
        {
            var setNumber = redisSetStudy.SetLength(defaultRedisKey);
            Assert.Equal(0, setNumber);
        }

        [Fact]
        public void SetLengthTest()
        {
            RedisValue[] values = new RedisValue[] 
            {
                "first",
                "second",
                "third",
                "four",
            };
            redisSetStudy.SetAdd(defaultRedisKey, values);

            var setNumber = redisSetStudy.SetLength(defaultRedisKey);
            Assert.Equal(4, setNumber);

            redisSetStudy.SetAdd(defaultRedisKey, "five");
            setNumber = redisSetStudy.SetLength(defaultRedisKey);
            Assert.Equal(5, setNumber);
        }
        #endregion

        #region SetMembers

        [Fact]
        public void SetMembersNotKeyTest()
        {
            var members = redisSetStudy.SetMembers(defaultRedisKey);
            Assert.Empty(members);
        }

        [Fact]
        public void SetMembersTest()
        {
            RedisValue[] values = new RedisValue[] 
            {
                "first",
                "second",
                "third",
                "four"
            };
            redisSetStudy.SetAdd(defaultRedisKey, values);

            var members = redisSetStudy.SetMembers(defaultRedisKey);
            Assert.Equal(4, members.Length);

            Assert.Contains(values[0], members);
            Assert.Contains(values[1], members);
            Assert.Contains(values[2], members);
            Assert.Contains(values[3], members);
        }
        #endregion

        #region SetRandomMember

        [Fact]
        public void SetRandomMemberTest()
        {
            var member = redisSetStudy.SetRandomMember(defaultRedisKey);
            Assert.False(member.HasValue);

            RedisValue[] values = new RedisValue[] 
            {
                "first","second","third","four",
            };
            redisSetStudy.SetAdd(defaultRedisKey, values);

            member = redisSetStudy.SetRandomMember(defaultRedisKey);
            Assert.True(member.HasValue);
        }
        #endregion

        #region SetRandomMembers
        [Fact]
        public void SetRandomMembersTest()
        {
            var members = redisSetStudy.SetRandomMembers(defaultRedisKey, 200);
            Assert.Empty(members);

            RedisValue[] values = new RedisValue[]
            {"first","second","third","four" };
            redisSetStudy.SetAdd(defaultRedisKey, values);

            members = redisSetStudy.SetRandomMembers(defaultRedisKey, 1);
            Assert.Equal(1, members.Length);

            members = redisSetStudy.SetRandomMembers(defaultRedisKey, 2);
            Assert.Equal(2, members.Length);

            members = redisSetStudy.SetRandomMembers(defaultRedisKey, 3);
            Assert.Equal(3, members.Length);

            members = redisSetStudy.SetRandomMembers(defaultRedisKey, 4);
            Assert.Equal(4, members.Length);

            members = redisSetStudy.SetRandomMembers(defaultRedisKey, 200);
            Assert.Equal(4, members.Length);
        }
        #endregion

        #region SetPop
        [Fact]
        public void SetPopNotKeyTest()
        {
            var member = redisSetStudy.SetPop(defaultRedisKey);
            Assert.False(member.HasValue);
        }

        [Fact]
        public void SetPopTest()
        {
            RedisValue[] values = new RedisValue[] 
            {
                "first","second","third","four"
            };
            redisSetStudy.SetAdd(defaultRedisKey, values);

            var member = redisSetStudy.SetPop(defaultRedisKey);
            Assert.True(member.HasValue);

            member = redisSetStudy.SetPop(defaultRedisKey);
            Assert.True(member.HasValue);

            member = redisSetStudy.SetPop(defaultRedisKey);
            Assert.True(member.HasValue);

            member = redisSetStudy.SetPop(defaultRedisKey);
            Assert.True(member.HasValue);

            member = redisSetStudy.SetPop(defaultRedisKey);
            Assert.False(member.HasValue);
        }
        #endregion

        #region SetRemove

        [Fact]
        public void SetRemoveNotKeyTest()
        {
            var result = redisSetStudy.SetRemove(defaultRedisKey, "first");
            Assert.False(result);

            var removeNumber = redisSetStudy.SetRemove(defaultRedisKey, new RedisValue[] { "first","second"});
            Assert.Equal(0, removeNumber);
        }

        [Fact]
        public void SetRemoveTest()
        {
            RedisValue[] values = new RedisValue[] 
            {
                "first","second","third","four","five"
            };
            redisSetStudy.SetAdd(defaultRedisKey, values);

            var removeOne = redisSetStudy.SetRemove(defaultRedisKey, "first");
            Assert.True(removeOne);

            var removNumber = redisSetStudy.SetRemove(defaultRedisKey, new RedisValue[] { "second", "third" });
            Assert.Equal(2, removNumber);

            removNumber = redisSetStudy.SetRemove(defaultRedisKey, new RedisValue[] { "third", "four" });
            Assert.Equal(1, removNumber);

            Assert.Equal(1, redisSetStudy.SetLength(defaultRedisKey));
        }
        #endregion

        #region SetMove

        [Fact]
        public void SetMoveNotSourceTest()
        {
            var result = redisSetStudy.SetMove(defaultRedisKey, defaultRedisKey + "2", "first");

            Assert.False(result);
        }

        [Fact]
        public void SetMoveNotDestinationTest()
        {
            var source_key = defaultRedisKey;
            var distination_key = defaultRedisKey + "2";

            redisSetStudy.SetAdd(source_key, "first");

            var result = redisSetStudy.SetMove(source_key, distination_key, "first");
            Assert.True(result);

            var first_in_source = redisSetStudy.SetContains(source_key, "first");
            Assert.False(first_in_source);

            var first_in_destonation= redisSetStudy.SetContains(distination_key, "first");
            Assert.True(first_in_destonation);

            redisDatabase.KeyDelete(source_key);
            redisDatabase.KeyDelete(distination_key);
        }

        [Fact]
        public void SetMoveTest()
        {
            var source_key = defaultRedisKey;
            var distination_key = defaultRedisKey + "2";
            redisSetStudy.SetAdd(source_key, new RedisValue[] { "first","second","third"});
            redisSetStudy.SetAdd(distination_key, "distination");

            var result = redisSetStudy.SetMove(source_key, distination_key, "first");
            Assert.True(result);

            var first_in_source = redisSetStudy.SetContains(source_key, "first");
            Assert.False(first_in_source);

            var first_in_destonation = redisSetStudy.SetContains(distination_key, "first");
            Assert.True(first_in_destonation);

            redisDatabase.KeyDelete(source_key);
            redisDatabase.KeyDelete(distination_key);
        }
        #endregion

        #region SetCombine

        [Fact]
        public void SetCombine_NullSet_Test()
        {
            RedisKey[] keys = new RedisKey[] 
            {
                defaultRedisKey,
                defaultRedisKey + "2"
            };

            RedisValue[] redisValues = redisSetStudy.SetCombine(SetOperation.Difference, keys);
            Assert.Empty(redisValues);

            redisValues = redisSetStudy.SetCombine(SetOperation.Intersect, keys);
            Assert.Empty(redisValues);

            redisValues = redisSetStudy.SetCombine(SetOperation.Union, keys);
            Assert.Empty(redisValues);

            foreach (var key in keys)
            {
                redisDatabase.KeyDelete(key);
            }
        }

        /// <summary>
        /// 差集运算 测试
        /// (注意:集合顺序影响结果)
        /// </summary>
        [Fact]
        public void SetCombine_Difference_Test()
        {
            //准备数据
            RedisKey[] keys = new RedisKey[]
            {
                defaultRedisKey,
                defaultRedisKey + "2"
            };
            redisSetStudy.SetAdd(keys[0], new RedisValue[] { 1,2,3,4,5,7,8,9});
            redisSetStudy.SetAdd(keys[1], new RedisValue[] { 2,4,6,8,10 });

            //keys的集合中第一个集合与后续集合的差集
            RedisValue[] redisValues = redisSetStudy.SetCombine(SetOperation.Difference, keys);
            Assert.Equal(new RedisValue[] { 1, 3, 5, 7, 9}, redisValues);

            //反转顺序,影响结果
            keys =  keys.Reverse().ToArray();
            redisValues = redisSetStudy.SetCombine(SetOperation.Difference, keys);
            Assert.Equal(new RedisValue[] { 6,10 }, redisValues);

            //清理
            foreach (var key in keys)
            {
                redisDatabase.KeyDelete(key);
            }
        }

        /// <summary>
        /// 交集运算 测试
        /// </summary>
        [Fact]
        public void SetCombine_Intersect_Test()
        {
            //准备数据
            RedisKey[] keys = new RedisKey[]
            {
                defaultRedisKey,
                defaultRedisKey + "2"
            };
            redisSetStudy.SetAdd(keys[0], new RedisValue[] { 1, 2, 3, 4, 5, 7, 8, 9 });
            redisSetStudy.SetAdd(keys[1], new RedisValue[] { 2, 4, 6, 8, 10 });

            //集合的交集
            RedisValue[] redisValues = redisSetStudy.SetCombine(SetOperation.Intersect, keys);
            Assert.Equal(new RedisValue[] { 2, 4, 8 }, redisValues);

            //反转顺序,并不影响结果
            keys = keys.Reverse().ToArray();
            redisValues = redisSetStudy.SetCombine(SetOperation.Intersect, keys);
            Assert.Equal(new RedisValue[] { 2, 4, 8 }, redisValues);

            //清理
            foreach (var key in keys)
            {
                redisDatabase.KeyDelete(key);
            }
        }

        /// <summary>
        /// 并集运算 测试
        /// </summary>
        [Fact]
        public void SetCombine_Union_Test()
        {
            //准备数据
            RedisKey[] keys = new RedisKey[]
            {
                defaultRedisKey,
                defaultRedisKey + "2"
            };
            redisSetStudy.SetAdd(keys[0], new RedisValue[] { 1, 2, 3, 4, 5, 7, 8, 9 });
            redisSetStudy.SetAdd(keys[1], new RedisValue[] { 2, 4, 6, 8, 10 });

            //集合的并集
            RedisValue[] redisValues = redisSetStudy.SetCombine(SetOperation.Union, keys);
            Assert.Equal(new RedisValue[] { 1,2,3,4,5,6,7,8,9,10 }, redisValues);

            //反转顺序,并不影响结果
            keys = keys.Reverse().ToArray();
            redisValues = redisSetStudy.SetCombine(SetOperation.Union, keys);
            Assert.Equal(new RedisValue[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, redisValues);

            //清理
            foreach (var key in keys)
            {
                redisDatabase.KeyDelete(key);
            }
        }

        /// <summary>
        /// 并集运算 测试
        /// </summary>
        [Fact]
        public void SetCombine_Overload_Test()
        {
            //准备数据
            RedisKey[] keys = new RedisKey[]
            {
                defaultRedisKey,
                defaultRedisKey + "2"
            };
            redisSetStudy.SetAdd(keys[0], new RedisValue[] { 1, 2, 3, 4, 5, 7, 8, 9 });
            redisSetStudy.SetAdd(keys[1], new RedisValue[] { 2, 4, 6, 8, 10 });

            //集合的并集
            RedisValue[] redisValues = redisSetStudy.SetCombine(SetOperation.Union, keys[0],keys[1]);
            Assert.Equal(new RedisValue[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, redisValues);

            //清理
            foreach (var key in keys)
            {
                redisDatabase.KeyDelete(key);
            }
        }
        #endregion

        #region SetCombineAndStore

        [Fact]
        public void SetCombineAndStore_NotKey_Test()
        {
            var firstKey = defaultRedisKey;
            var secondKey = defaultRedisKey + "2";
            var desinationKey = defaultRedisKey + "desination";

            var result = redisSetStudy.SetCombineAndStore(SetOperation.Difference, defaultRedisKey, firstKey, secondKey);
            Assert.Equal(0, result);

            var desination_exits = redisDatabase.KeyExists(desinationKey);
            Assert.False(desination_exits);
        }

        /// <summary>
        /// 差集
        /// </summary>
        [Fact]
        public void SetCombineAndStore_Difference_Test()
        {
            //准备
            var firstKey = defaultRedisKey;
            var secondKey = defaultRedisKey + "2";
            var desinationKey = defaultRedisKey + "desination";
            redisSetStudy.SetAdd(firstKey, new RedisValue[] {  2, 4, 5, 7, 8, 9 });
            redisSetStudy.SetAdd(secondKey, new RedisValue[] { 2, 4, 6, 8, 10 });

            //操作与断言
            var result = redisSetStudy.SetCombineAndStore(SetOperation.Difference, desinationKey, firstKey, secondKey);
            Assert.Equal(3, result);

            var desination_members = redisSetStudy.SetMembers(desinationKey);
            Assert.Equal(new RedisValue[] {5,7,9},desination_members);

            //交换集合顺序,影响结果
            result = redisSetStudy.SetCombineAndStore(SetOperation.Difference, desinationKey,new RedisKey[]{secondKey,firstKey });
            Assert.Equal(2, result);

            desination_members = redisSetStudy.SetMembers(desinationKey);
            Assert.Equal(new RedisValue[] { 6, 10 }, desination_members);

            //清理
            redisDatabase.KeyDelete(firstKey);
            redisDatabase.KeyDelete(secondKey);
            redisDatabase.KeyDelete(desinationKey);
        }

        /// <summary>
        /// 交集
        /// </summary>
        [Fact]
        public void SetCombineAndStore_Intersect_Test()
        {
            //准备
            var firstKey = defaultRedisKey;
            var secondKey = defaultRedisKey + "2";
            var desinationKey = defaultRedisKey + "desination";
            redisSetStudy.SetAdd(firstKey, new RedisValue[] { 2, 4, 5, 7, 8, 9 });
            redisSetStudy.SetAdd(secondKey, new RedisValue[] { 2, 4, 6, 8, 10 });

            //操作与断言
            var result = redisSetStudy.SetCombineAndStore(SetOperation.Intersect, desinationKey, firstKey, secondKey);
            Assert.Equal(3, result);

            var desination_members = redisSetStudy.SetMembers(desinationKey);
            Assert.Equal(new RedisValue[] { 2, 4, 8 }, desination_members);

            //清理
            redisDatabase.KeyDelete(firstKey);
            redisDatabase.KeyDelete(secondKey);
            redisDatabase.KeyDelete(desinationKey);
        }

        /// <summary>
        /// 并集
        /// </summary>
        [Fact]
        public void SetCombineAndStore_Union_Test()
        {
            //准备
            var firstKey = defaultRedisKey;
            var secondKey = defaultRedisKey + "2";
            var desinationKey = defaultRedisKey + "desination";
            redisSetStudy.SetAdd(firstKey, new RedisValue[] { 2, 4, 5, 7, 8, 9 });
            redisSetStudy.SetAdd(secondKey, new RedisValue[] { 2, 4, 6, 8, 10 });

            //操作与断言
            var result = redisSetStudy.SetCombineAndStore(SetOperation.Union, desinationKey, firstKey, secondKey);
            Assert.Equal(8, result);

            var desination_members = redisSetStudy.SetMembers(desinationKey);
            Assert.Equal(new RedisValue[] { 2, 4, 5, 6, 7, 8, 9, 10 }, desination_members);

            //清理
            redisDatabase.KeyDelete(firstKey);
            redisDatabase.KeyDelete(secondKey);
            redisDatabase.KeyDelete(desinationKey);
        }
        #endregion

        #region SetScan 简单测试,有专注测试Scan方法的类
        [Fact]
        public void SetScanTest()
        {
            var values = new RedisValue[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

            redisSetStudy.SetAdd(defaultRedisKey,values);

            var members = redisSetStudy.SetScan(defaultRedisKey, "*", 200,CommandFlags.None);
            var membaerList = members.ToList();
            Assert.Equal(values, membaerList);
        }

        [Fact]
        public void SetScan_OverLoad_Test()
        {
            var values = new RedisValue[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

            redisSetStudy.SetAdd(defaultRedisKey, values);

            var members = redisSetStudy.SetScan(defaultRedisKey, "*", 100, 0, 0, CommandFlags.None);
            var membaerList = members.ToList();
            Assert.Equal(values, membaerList);
        }
        #endregion

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