using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;

using ThingsGateway.NewLife.Collections;
using ThingsGateway.NewLife.Configuration;
using ThingsGateway.NewLife.Data;
using ThingsGateway.NewLife.Log;
using ThingsGateway.NewLife.Model;
using ThingsGateway.NewLife.Net;
using ThingsGateway.NewLife.Reflection;
using ThingsGateway.NewLife.Security;
using ThingsGateway.NewLife.Serialization;

namespace ThingsGateway.NewLife.Caching;

/// <summary>Redis客户端</summary>
/// <remarks>
/// 文档 https://newlifex.com/core/redis
/// 
/// 强烈建议保持唯一的Redis对象供多次使用，Redis内部有连接池并且支持多线程并发访问。
/// 高级功能需要引用NewLife.Redis，然后实例化FullRedis类。
/// 
/// 网络层Broken pipe异常，可以在Server设置多个一样的地址（逗号隔开），让Redis客户端在遇到网络错误时进行重试。
/// </remarks>
public abstract class Redis : Cache, IConfigMapping, ILogFeature
{
    #region 属性
    /// <summary>服务器，带端口。例如127.0.0.1:6397，支持逗号分隔的多地址，网络异常时，自动切换到其它节点，60秒后切回来</summary>
    public String? Server { get; set; }

    /// <summary>用户名。Redis6.0支持</summary>
    public String? UserName { get; set; }

    /// <summary>密码</summary>
    public String? Password { get; set; }

    /// <summary>目标数据库。默认0</summary>
    public Int32 Db { get; set; }

    /// <summary>读写超时时间。默认3000ms</summary>
    public Int32 Timeout { get; set; } = 3_000;

    /// <summary>出错重试次数。如果出现错误，可以重试的次数，默认3</summary>
    public Int32 Retry { get; set; } = 3;

    /// <summary>不可用节点的屏蔽时间。默认10秒</summary>
    public Int32 ShieldingTime { get; set; } = 10;

    /// <summary>完全管道。读取操作是否合并进入管道，默认false</summary>
    public Boolean FullPipeline { get; set; }

    /// <summary>自动管道。管道操作达到一定数量时，自动提交，默认0</summary>
    public Int32 AutoPipeline { get; set; }

    /// <summary>编码器。决定对象存储在redis中的格式，默认json</summary>
    public IPacketEncoder Encoder { get; set; } = new RedisJsonEncoder();

    /// <summary>Json序列化主机</summary>
    public IJsonHost JsonHost { get; set; } = null!;

    /// <summary>SSL协议。决定是否启用SSL连接，默认None不启用</summary>
    public SslProtocols SslProtocol { get; set; } = SslProtocols.None;

    /// <summary>X509证书。用于SSL连接时验证证书指纹，可以直接加载pem证书文件，未指定时不验证证书</summary>
    /// <remarks>var cert = new X509Certificate2("abc.pem", "pass");</remarks>
    public X509Certificate2? Certificate { get; set; }

    /// <summary>失败时抛出异常。默认true</summary>
    public Boolean ThrowOnFailure { get; set; } = true;

    /// <summary>最大消息大小。超过时抛出异常，默认1024*1024字节，行业标准是32k算大Value，这里超过1m报错</summary>
    public Int32 MaxMessageSize { get; set; } = 1024 * 1024;

    /// <summary>性能计数器</summary>
    public PerfCounter? Counter { get; set; }

    /// <summary>性能跟踪器。仅记录read/write，形成调用链，key在tag中，没有记录异常。高速海量操作时不建议开启</summary>
    public ITracer? Tracer { get; set; }

    private IDictionary<String, String>? _Info;
    /// <summary>服务器信息</summary>
    public IDictionary<String, String> Info => _Info ??= GetInfo();

    private Version? _Version;
    /// <summary>Redis版本。可用于判断某些指令是否可用</summary>
    public Version Version
    {
        get
        {
            if (_Version == null)
            {
                var inf = Info;
                if (inf != null && inf.TryGetValue("redis_version", out var ver))
                {
                    if (!ver.IsNullOrEmpty() && Version.TryParse(ver, out var version))
                        _Version = version;
                }
                _Version ??= new Version();
            }

            return _Version;
        }
    }
    #endregion

    #region 构造
    /// <summary>实例化</summary>
    public Redis() : base()
    {
        // 初始化Json序列化
        JsonHost ??= RedisJsonEncoder.GetJsonHost();
    }

    /// <summary>实例化Redis，指定服务器地址、密码、库</summary>
    /// <param name="server"></param>
    /// <param name="password"></param>
    /// <param name="db"></param>
    public Redis(String server, String password, Int32 db) : this()
    {
        // 有人多输入了一个空格，酿成大祸
        Server = server?.Trim();
        Password = password?.Trim();
        Db = db;
    }

    /// <summary>实例化Redis，指定服务器地址、用户、密码、库</summary>
    /// <param name="server"></param>
    /// <param name="username"></param>
    /// <param name="password"></param>
    /// <param name="db"></param>
    public Redis(String server, String username, String password, Int32 db) : this()
    {
        // 有人多输入了一个空格，酿成大祸
        Server = server?.Trim();
        UserName = username?.Trim();
        Password = password?.Trim();
        Db = db;
    }

    /// <summary>按照配置服务实例化Redis，用于NETCore依赖注入</summary>
    /// <param name="provider">服务提供者，将要解析IConfigProvider</param>
    /// <param name="name">缓存名称，也是配置中心key</param>
    public Redis(IServiceProvider provider, String name) : this()
    {
        Name = name;
        Tracer = provider.GetService<ITracer>();
        var log = provider.GetService<ILog>();
        if (log != null) Log = log;

        var config = provider.GetService<IConfigProvider>();
        config ??= JsonConfigProvider.LoadAppSettings();
        config.Bind(this, true, name);
    }

    /// <summary>实例化Redis，指定名称，支持从环境变量Redis_{Name}读取配置，或者逐个属性配置</summary>
    /// <param name="name"></param>
    public Redis(String name) : this()
    {
        if (name.IsNullOrEmpty()) throw new ArgumentNullException(nameof(name));

        Name = name;

        // 从环境变量加载连接字符
        foreach (DictionaryEntry item in Environment.GetEnvironmentVariables())
        {
            if (item.Key is String key && item.Value is String value && key.EqualIgnoreCase($"Redis_{name}"))
            {
                Init(value);
            }
        }
    }

    /// <summary>销毁</summary>
    /// <param name="disposing"></param>
    protected override void Dispose(Boolean disposing)
    {
        base.Dispose(disposing);

        try
        {
            Commit();
        }
        catch { }

        _Pool.TryDispose();
    }

    /// <summary>已重载。</summary>
    /// <returns></returns>
    public override String ToString() => $"{Name} Server={Server} Db={Db}";
    #endregion

    #region 配置方法
    private String? _configOld;
    /// <summary>使用连接字符串初始化</summary>
    /// <param name="config"></param>
    public override void Init(String config)
    {
        if (config.IsNullOrEmpty()) return;

        if (config == _configOld) return;

        if (!_configOld.IsNullOrEmpty() && XTrace.Log.Level <= LogLevel.Debug)
            XTrace.WriteLine("Redis[{0}]连接字符串改变！", Name);

        // 解密连接字符串中被保护的密码。解密密钥位于配置文件ProtectedKey，或者环境变量中
        var connStr = config;
        var pk = ProtectedKey.Instance;
        if (pk?.Secret != null) connStr = pk.Unprotect(connStr);

        // 默认分号分割，旧版逗号分隔。可能只有一个server=后续多个含逗号的地址
        var dic = ParseConfig(connStr);
        if (dic.Count > 0)
        {
            Server = dic["Server"]?.Trim();
            UserName = dic["UserName"]?.Trim();
            Password = dic["Password"]?.Trim();
            //Db = dic["Db"].ToInt();
            if (dic.TryGetValue("Db", out var str))
                Db = str.ToInt();

            if (Server.IsNullOrEmpty() && dic.TryGetValue("[0]", out var svr)) Server = svr;

            // 连接字符串可能独立写了port
            var port = dic["Port"].ToInt();
            if (port > 0 && !Server.IsNullOrEmpty() && !Server.Contains(':')) Server += ":" + port;

            if (dic.TryGetValue("Timeout", out str))
                Timeout = str.ToInt();
            else if (dic.TryGetValue("responseTimeout", out str))
                Timeout = str.ToInt();
            else if (dic.TryGetValue("connectTimeout", out str))
                Timeout = str.ToInt();

            if (dic.TryGetValue("ThrowOnFailure", out str))
                ThrowOnFailure = str.ToBoolean();

            if (dic.TryGetValue("MaxMessageSize", out str) && str.ToInt(-1) >= 0)
                MaxMessageSize = str.ToInt();

            if (dic.TryGetValue("Expire", out str) && str.ToInt(-1) >= 0)
                Expire = str.ToInt();
        }

        // 更换Redis连接字符串时，清空原连接池
        _servers = null;
        if (!_configOld.IsNullOrEmpty())
        {
            _Pool = null;
            _Info = null;
        }

        _configOld = config;

        // 初始化Json序列化
        JsonHost ??= RedisJsonEncoder.GetJsonHost();
        if (Encoder is RedisJsonEncoder encoder)
            encoder.JsonHost = JsonHost;
    }

    /// <summary>分析配置连接字符串</summary>
    /// <param name="connStr"></param>
    /// <returns></returns>
    protected IDictionary<String, String> ParseConfig(String connStr)
    {
        // 默认分号分割，旧版逗号分隔。可能只有一个server=后续多个含逗号的地址
        var dic =
            connStr.Contains(';') || connStr.Split('=').Length <= 2 ?
            connStr.SplitAsDictionary("=", ";", true) :
            connStr.SplitAsDictionary("=", ",", true);

        return dic;
    }

    void IConfigMapping.MapConfig(IConfigProvider provider, IConfigSection section)
    {
        if (section?.Value != null) Init(section.Value);
    }
    #endregion

    #region 客户端池
    private class MyPool : ObjectPool<RedisClient>
    {
        public Redis Instance { get; set; } = null!;

        public Func<RedisClient> Callback { get; set; } = null!;

        protected override RedisClient OnCreate() => Callback();

        protected override Boolean OnGet(RedisClient value)
        {
            // 借出时清空残留
            value.Reset();

            return base.OnGet(value);
        }
    }

    private NetUri[]? _servers;
    private Int32 _idxServer;
    private Int32 _idxLast = -1;
    private DateTime _nextTrace;

    /// <summary>获取解析后的地址列表</summary>
    /// <returns></returns>
    public NetUri[] GetServices()
    {
        // 初始化服务器地址列表
        var svrs = _servers;
        if (svrs != null) return svrs;

        var server = Server?.Trim();
        if (server.IsNullOrEmpty()) throw new ArgumentNullException(nameof(Server));

        var ss = server.Split(',', StringSplitOptions.RemoveEmptyEntries);
        var uris = new NetUri[ss.Length];
        for (var i = 0; i < ss.Length; i++)
        {
            var svr2 = ss[i];
            if (!svr2.Contains("://")) svr2 = "tcp://" + svr2;

            var uri = new NetUri(svr2);
            if (uri.Port == 0) uri.Port = 6379;
            uris[i] = uri;
        }

        return _servers = uris;
    }

    /// <summary>设置服务端地址列表</summary>
    /// <param name="services"></param>
    public void SetSevices(NetUri[] services) => _servers = services;

    /// <summary>创建连接客户端</summary>
    /// <returns></returns>
    protected virtual RedisClient OnCreate()
    {
        var server = Server?.Trim();
        if (server.IsNullOrEmpty()) throw new ArgumentNullException(nameof(Server));

        // 初始化服务器地址列表
        var svrs = GetServices();

        // 一定时间后，切换回来主节点
        var idx = _idxServer;
        if (idx > 0)
        {
            if (idx >= svrs.Length) idx %= svrs.Length;

            var now = DateTime.Now;
            if (_nextTrace.Year < 2000) _nextTrace = now.AddSeconds(300);
            if (now > _nextTrace)
            {
                _nextTrace = DateTime.MinValue;

                idx = _idxServer = 0;
            }
        }

        if (idx != _idxLast)
        {
            WriteLog("使用Redis服务器：{0}", svrs[idx % svrs.Length]);

            _idxLast = idx;
        }

        // 选择服务器，修改实例名，后面用于埋点
        var svr = svrs[idx % svrs.Length];
        if (Name.IsNullOrEmpty() || Name.EqualIgnoreCase("Redis", "FullRedis")) Name = svr.Host ?? svr.Address.ToString();

        var rc = new RedisClient(this, svr)
        {
            Log = ClientLog
        };
        //if (rds.Db > 0) rc.Select(rds.Db);

        return rc;
    }

    private IPool<RedisClient>? _Pool;
    /// <summary>连接池</summary>
    public IPool<RedisClient> Pool
    {
        get
        {
            if (_Pool != null) return _Pool;
            lock (this)
            {
                if (_Pool != null) return _Pool;

                return _Pool = CreatePool(OnCreate);
            }
        }
    }

    /// <summary>创建连接池</summary>
    /// <param name="onCreate"></param>
    /// <returns></returns>
    protected virtual IPool<RedisClient> CreatePool(Func<RedisClient> onCreate)
    {
        var pool = new MyPool
        {
            Name = Name + "Pool",
            Instance = this,
            Min = 10,
            Max = 100000,
            IdleTime = 30,
            AllIdleTime = 300,
            Log = ClientLog,

            Callback = onCreate,
        };

        return pool;
    }
    #endregion

    #region 方法
    /// <summary>执行命令，经过管道。FullRedis中还会考虑Cluster分流</summary>
    /// <typeparam name="TResult">返回类型</typeparam>
    /// <param name="key">命令key，用于选择集群节点</param>
    /// <param name="func">回调函数</param>
    /// <param name="write">是否写入操作</param>
    /// <returns></returns>
    public virtual TResult Execute<TResult>(String key, Func<RedisClient, String, TResult> func, Boolean write = false)
    {
        // 写入或完全管道模式时，才处理管道操作
        if (write || FullPipeline)
        {
            // 管道模式直接执行
            var rds = _client.Value;
            if (rds == null && AutoPipeline > 0) rds = StartPipeline();
            if (rds != null)
            {
                var rs = func(rds, key);

                // 命令数足够，自动提交
                if (AutoPipeline > 0 && rds.PipelineCommands >= AutoPipeline)
                {
                    StopPipeline(true);
                    StartPipeline();
                }

                return rs;
            }
        }

        // 读操作遇到未完成管道队列时，立马执行管道操作
        if (!write) StopPipeline(true);

        // 统计性能
        var sw = Counter?.StartCount();

        var i = 0;
        var delay = 500;
        do
        {
            // 每次重试都需要重新从池里借出连接
            var client = Pool.Get();
            try
            {
                client.Reset();
                return func(client, key);
            }
            catch (RedisException) { throw; }
            catch (Exception ex)
            {
                if (++i >= Retry) throw;

                // 销毁连接
                client.TryDispose();

                // 网络异常时，自动切换到其它节点
                if (ex is AggregateException ae) ex = ae.InnerException;
                if (NoDelay(ex))
                {
                    if (_servers == null || i >= _servers.Length) throw;
                    _idxServer++;
                }
                else
                    Thread.Sleep(delay *= 2);
            }
            finally
            {
                Pool.Return(client);

                Counter?.StopCount(sw);
            }
        } while (true);
    }

    /// <summary>网络IO类异常，无需等待，因为遇到此类异常时重发请求也是错</summary>
    /// <param name="ex"></param>
    /// <returns></returns>
    internal Boolean NoDelay(Exception ex) => ex is SocketException or IOException or InvalidOperationException;

    /// <summary>直接执行命令，不考虑集群读写</summary>
    /// <typeparam name="TResult">返回类型</typeparam>
    /// <param name="func">回调函数</param>
    /// <returns></returns>
    public virtual TResult Execute<TResult>(Func<RedisClient, TResult> func)
    {
        // 统计性能
        var sw = Counter?.StartCount();

        var i = 0;
        var delay = 500;
        do
        {
            // 每次重试都需要重新从池里借出连接
            var pool = Pool;
            var client = pool.Get();
            try
            {
                client.Reset();
                return func(client);
            }
            catch (RedisException) { throw; }
            catch (Exception ex)
            {
                if (++i >= Retry) throw;

                // 销毁连接
                client.TryDispose();

                // 网络异常时，自动切换到其它节点
                if (ex is AggregateException ae) ex = ae.InnerException;
                if (NoDelay(ex))
                {
                    if (_servers == null || i >= _servers.Length) throw;
                    _idxServer++;
                }
                else
                    Thread.Sleep(delay *= 2);
            }
            finally
            {
                pool.Return(client);

                Counter?.StopCount(sw);
            }
        } while (true);
    }

    /// <summary>异步执行命令，经过管道。FullRedis中还会考虑Cluster分流</summary>
    /// <typeparam name="TResult">返回类型</typeparam>
    /// <param name="key">命令key，用于选择集群节点</param>
    /// <param name="func">回调函数</param>
    /// <param name="write">是否写入操作</param>
    /// <returns></returns>
    public virtual async Task<TResult> ExecuteAsync<TResult>(String key, Func<RedisClient, String, Task<TResult>> func, Boolean write = false)
    {
        // 写入或完全管道模式时，才处理管道操作
        if (write || FullPipeline)
        {
            // 管道模式直接执行
            var rds = _client.Value;
            if (rds == null && AutoPipeline > 0) rds = StartPipeline();
            if (rds != null)
            {
                var rs = await func(rds, key).ConfigureAwait(false);

                // 命令数足够，自动提交
                if (AutoPipeline > 0 && rds.PipelineCommands >= AutoPipeline)
                {
                    StopPipeline(true);
                    StartPipeline();
                }

                return rs;
            }
        }

        // 读操作遇到未完成管道队列时，立马执行管道操作
        if (!write) StopPipeline(true);

        // 统计性能
        var sw = Counter?.StartCount();

        var i = 0;
        var delay = 500;
        do
        {
            // 每次重试都需要重新从池里借出连接
            var client = Pool.Get();
            try
            {
                client.Reset();
                return await func(client, key).ConfigureAwait(false);
            }
            catch (RedisException) { throw; }
            catch (Exception ex)
            {
                if (++i >= Retry) throw;

                // 销毁连接
                client.TryDispose();

                // 网络异常时，自动切换到其它节点
                if (ex is AggregateException ae) ex = ae.InnerException;
                if (NoDelay(ex))
                {
                    if (_servers == null || i >= _servers.Length) throw;
                    _idxServer++;
                }
                else
                    Thread.Sleep(delay *= 2);
            }
            finally
            {
                Pool.Return(client);

                Counter?.StopCount(sw);
            }
        } while (true);
    }

    private readonly ThreadLocal<RedisClient?> _client = new();
    /// <summary>开始管道模式</summary>
    public virtual RedisClient StartPipeline()
    {
        var rds = _client.Value;
        if (rds == null)
        {
            rds = Pool.Get();
            rds.Reset();
            rds.StartPipeline();

            _client.Value = rds;
        }

        return rds;
    }

    /// <summary>结束管道模式</summary>
    /// <param name="requireResult">要求结果。默认true</param>
    public virtual Object?[]? StopPipeline(Boolean requireResult = true)
    {
        var rds = _client.Value;
        if (rds == null) return null;
        _client.Value = null;

        // 统计性能
        var sw = Counter?.StartCount();

        // 管道处理不需要重试
        try
        {
            return rds.StopPipeline(requireResult);
        }
        finally
        {
            // 如果不需要结果，则暂停一会，有效清理残留
            if (!requireResult) Thread.Sleep(10);

            rds.Reset();
            Pool.Return(rds);

            Counter?.StopCount(sw);
        }
    }

    /// <summary>提交变更。处理某些残留在管道里的命令</summary>
    /// <returns></returns>
    public override Int32 Commit()
    {
        var rs = StopPipeline(true);
        if (rs == null) return 0;

        return rs.Length;
    }
    #endregion

    #region 子库
    /// <summary>为同一服务器创建不同Db的子级库</summary>
    /// <param name="db"></param>
    /// <returns></returns>
    public virtual Redis CreateSub(Int32 db)
    {
        var rds = (GetType().CreateInstance() as Redis)!;
        rds.Server = Server;
        rds.Db = db;
        rds.UserName = UserName;
        rds.Password = Password;

        rds.Encoder = Encoder;
        rds.JsonHost = JsonHost;
        rds.Timeout = Timeout;
        rds.Retry = Retry;
        rds.ShieldingTime = ShieldingTime;
        rds.FullPipeline = FullPipeline;
        rds.AutoPipeline = AutoPipeline;
        rds.SslProtocol = SslProtocol;
        rds.Certificate = Certificate;
        rds.ThrowOnFailure = ThrowOnFailure;
        rds.MaxMessageSize = MaxMessageSize;

        rds._Info = _Info;
        rds._Version = _Version;

        rds.Tracer = Tracer;
        rds.Log = Log;

        return rds;
    }
    #endregion

    #region 基础操作
    /// <summary>缓存个数</summary>
    public override Int32 Count => Execute(rds => rds.Execute<Int32>("DBSIZE"));

    /// <summary>获取所有键，限制10000项，超额请使用FullRedis.Search</summary>
    public override ICollection<String> Keys
    {
        get
        {
            if (Count > 10000) throw new InvalidOperationException("数量过大时，禁止获取所有键，请使用FullRedis.Search");

            return Execute(rds => rds.Execute<String[]>("KEYS", "*")) ?? [];
        }
    }

    /// <summary>获取信息</summary>
    /// <param name="all">是否获取全部信息，包括Commandstats</param>
    /// <returns></returns>
    public virtual IDictionary<String, String> GetInfo(Boolean all = false)
    {
        var rs = all ?
            Execute(rds => rds.Execute<String>("INFO", "all")) :
            Execute(rds => rds.Execute<String>("INFO"));
        if (rs.IsNullOrEmpty()) return new Dictionary<String, String>();

        //var inf = rs.ToStr();
        return rs.SplitAsDictionary(":", "\r\n");
    }

    /// <summary>单个实体项</summary>
    /// <param name="key">键</param>
    /// <param name="value">值</param>
    /// <param name="expire">过期时间，秒。小于0时采用默认缓存时间<seealso cref="Cache.Expire"/></param>
    public override Boolean Set<T>(String key, T value, Int32 expire = -1)
    {
        if (expire < 0) expire = Expire;

        var rs = "";
        if (expire <= 0)
            rs = Execute(key, (rds, k) => rds.Execute<String>("SET", k, value), true);
        else
            rs = Execute(key, (rds, k) => rds.Execute<String>("SETEX", k, expire, value), true);

        if (rs == "OK") return true;
        if (rs.IsNullOrEmpty()) return false;

        using var span = Tracer?.NewSpan($"redis:{Name}:ErrorSet", new { key, value });
        if (ThrowOnFailure) throw new XException("Redis.Set({0},{1})失败。{2}", key, value, rs);

        return false;
    }

    /// <summary>获取单体</summary>
    /// <param name="key">键</param>
    [return: MaybeNull]
    public override T Get<T>(String key) => Execute(key, (rds, k) => rds.Execute<T>("GET", k));

    /// <summary>移除缓存项</summary>
    /// <param name="key">键</param>
    public override Int32 Remove(String key)
    {
        if (key.IsNullOrEmpty()) return 0;

        return Execute(key, (rds, k) => rds.Execute<Int32>("DEL", k), true);
    }

    /// <summary>批量移除缓存项</summary>
    /// <param name="keys">键集合</param>
    public override Int32 Remove(params String[] keys)
    {
        if (keys == null || keys.Length <= 0) return 0;

        return Execute(keys.FirstOrDefault(), (rds, k) => rds.Execute<Int32>("DEL", keys), true);
    }

    /// <summary>清空所有缓存项</summary>
    public override void Clear() => Execute(rds => rds.Execute<String>("FLUSHDB"));

    /// <summary>是否存在</summary>
    /// <param name="key">键</param>
    public override Boolean ContainsKey(String key) => Execute(key, (rds, k) => rds.Execute<Int32>("EXISTS", k) > 0);

    /// <summary>设置缓存项有效期</summary>
    /// <param name="key">键</param>
    /// <param name="expire">过期时间</param>
    public override Boolean SetExpire(String key, TimeSpan expire) => Execute(key, (rds, k) => rds.Execute<String>("EXPIRE", k, (Int32)expire.TotalSeconds) == "1", true);

    /// <summary>获取缓存项有效期</summary>
    /// <param name="key">键</param>
    /// <returns></returns>
    public override TimeSpan GetExpire(String key)
    {
        var sec = Execute(key, (rds, k) => rds.Execute<Int32>("TTL", k));
        return TimeSpan.FromSeconds(sec);
    }
    #endregion

    #region 集合操作
    /// <summary>批量获取缓存项</summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="keys"></param>
    /// <returns></returns>
    public override IDictionary<String, T> GetAll<T>(IEnumerable<String> keys)
    {
        var ks = keys as String[] ?? keys.ToArray();
        if (ks.Length == 0) return new Dictionary<String, T>();

        return Execute(ks[0], (rds, k) => rds.GetAll<T>(ks));
    }

    /// <summary>批量设置缓存项</summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="values"></param>
    /// <param name="expire">过期时间，秒。小于0时采用默认缓存时间<seealso cref="Cache.Expire"/></param>
    public override void SetAll<T>(IDictionary<String, T> values, Int32 expire = -1)
    {
        if (values == null || values.Count == 0) return;

        if (expire < 0) expire = Expire;

        // 优化少量读取
        if (values.Count <= 2)
        {
            foreach (var item in values)
            {
                Set(item.Key, item.Value, expire);
            }
            return;
        }

        Execute(values.FirstOrDefault().Key, (rds, k) => rds.SetAll(values), true);

        // 使用管道批量设置过期时间
        if (expire > 0)
        {
            var ts = TimeSpan.FromSeconds(expire);

            StartPipeline();
            try
            {
                foreach (var item in values)
                {
                    SetExpire(item.Key, ts);
                }
            }
            finally
            {
                StopPipeline(true);
            }
        }
    }

    /// <summary>获取哈希</summary>
    /// <typeparam name="T">元素类型</typeparam>
    /// <param name="key">键</param>
    /// <returns></returns>
    public override IDictionary<String, T> GetDictionary<T>(String key) => throw new NotSupportedException("Redis未支持该功能，需要new FullRedis");

    /// <summary>获取队列</summary>
    /// <typeparam name="T">元素类型</typeparam>
    /// <param name="key">键</param>
    /// <returns></returns>
    public override IProducerConsumer<T> GetQueue<T>(String key) => throw new NotSupportedException("Redis未支持该功能，需要new FullRedis");

    /// <summary>获取栈</summary>
    /// <typeparam name="T">元素类型</typeparam>
    /// <param name="key">键</param>
    /// <returns></returns>
    public override IProducerConsumer<T> GetStack<T>(String key) => throw new NotSupportedException("Redis未支持该功能，需要new FullRedis");

    /// <summary>获取Set</summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="key"></param>
    /// <returns></returns>
    public override ICollection<T> GetSet<T>(String key) => throw new NotSupportedException("Redis未支持该功能，需要new FullRedis");
    #endregion


    #region 高级操作
    private static Version _v2612 = new("2.6.12");
    /// <summary>添加，已存在时不更新</summary>
    /// <typeparam name="T">值类型</typeparam>
    /// <param name="key">键</param>
    /// <param name="value">值</param>
    /// <param name="expire">过期时间，秒。小于0时采用默认缓存时间<seealso cref="Cache.Expire"/></param>
    /// <returns></returns>
    public override Boolean Add<T>(String key, T value, Int32 expire = -1)
    {
        if (expire < 0) expire = Expire;

        // 没有有效期，直接使用SETNX
        if (expire <= 0) return Execute(key, (rds, k) => rds.Execute<Int32>("SETNX", k, value), true) > 0;

        // 带有有效期，需要判断版本是否支持
        var inf = Info;
        //if (inf != null && inf.TryGetValue("redis_version", out var ver) && ver.CompareTo("4.") >= 0 && ver.CompareTo("6.") < 0)
        //{
        //    return Execute(key, rds => rds.Execute<Int32>("SETNX", key, value, expire), true) > 0;
        //}
        if (Version >= _v2612)
        {
            //!!! 重构Redis.Add实现，早期的SETNX支持设置过期时间，后来不支持了，并且连资料都找不到了，改用2.6.12新版 SET key value EX expire NX
            var result = Execute(key, (rds, k) => rds.Execute<String>("SET", k, value, "EX", expire, "NX"), true);
            if (result.IsNullOrEmpty()) return false;
            if (result == "OK") return true;

            using var span = Tracer?.NewSpan($"redis:{Name}:ErrorAdd", new { key, value });
            if (ThrowOnFailure) throw new XException("Redis.Add({0},{1})失败。{2}", key, value, result);

            return false;
        }

        // 旧版本不支持SETNX带过期时间，需要分为前后两条指令
        var rs = Execute(key, (rds, k) => rds.Execute<Int32>("SETNX", k, value), true);
        if (rs > 0) SetExpire(key, TimeSpan.FromSeconds(expire));

        return rs > 0;
    }

    /// <summary>设置新值并获取旧值，原子操作</summary>
    /// <typeparam name="T">值类型</typeparam>
    /// <param name="key">键</param>
    /// <param name="value">值</param>
    /// <returns></returns>
    [return: MaybeNull]
    public override T Replace<T>(String key, T value) => Execute(key, (rds, k) => rds.Execute<T>("GETSET", k, value), true);

    /// <summary>尝试获取指定键，返回是否包含值。有可能缓存项刚好是默认值，或者只是反序列化失败</summary>
    /// <remarks>
    /// 在 Redis 中，可能有key（此时TryGet返回true），但是因为反序列化失败，从而得不到value。
    /// </remarks>
    /// <typeparam name="T">值类型</typeparam>
    /// <param name="key">键</param>
    /// <param name="value">值。即使有值也不一定能够返回，可能缓存项刚好是默认值，或者只是反序列化失败</param>
    /// <returns>返回是否包含值，即使反序列化失败</returns>
    public override Boolean TryGetValue<T>(String key, [MaybeNullWhen(false)] out T value)
    {
        T? v1 = default;
        var rs1 = Execute(key, (rds, k) =>
        {
            var rs2 = rds.TryExecute("GET", [k], out T? v2);
            v1 = v2;
            return rs2;
        });
        value = v1;

        return rs1;
    }

    ///// <summary>获取 或 添加 缓存数据，在数据不存在时执行委托请求数据</summary>
    ///// <typeparam name="T"></typeparam>
    ///// <param name="key"></param>
    ///// <param name="callback"></param>
    ///// <returns></returns>
    //public override T GetOrAdd<T>(String key, Func<String, T> callback)
    //{
    //    var value = Get<T>(key);
    //    if (!Equals(value, default)) return value;

    //    if (ContainsKey(key)) return value;

    //    value = callback(key);

    //    if (Add(key, value)) return value;

    //    return Get<T>(key);
    //}

    /// <summary>累加，原子操作</summary>
    /// <param name="key">键</param>
    /// <param name="value">变化量</param>
    /// <returns></returns>
    public override Int64 Increment(String key, Int64 value)
    {
        if (value == 1)
            return Execute(key, (rds, k) => rds.Execute<Int64>("INCR", k), true);
        else
            return Execute(key, (rds, k) => rds.Execute<Int64>("INCRBY", k, value), true);
    }

    /// <summary>累加，原子操作，乘以100后按整数操作</summary>
    /// <param name="key">键</param>
    /// <param name="value">变化量</param>
    /// <returns></returns>
    public override Double Increment(String key, Double value) => Execute(key, (rds, k) => rds.Execute<Double>("INCRBYFLOAT", k, value), true);

    /// <summary>递减，原子操作</summary>
    /// <param name="key">键</param>
    /// <param name="value">变化量</param>
    /// <returns></returns>
    public override Int64 Decrement(String key, Int64 value)
    {
        if (value == 1)
            return Execute(key, (rds, k) => rds.Execute<Int64>("DECR", k), true);
        else
            return Execute(key, (rds, k) => rds.Execute<Int64>("DECRBY", k, value.ToString()), true);
    }

    /// <summary>递减，原子操作，乘以100后按整数操作</summary>
    /// <param name="key">键</param>
    /// <param name="value">变化量</param>
    /// <returns></returns>
    public override Double Decrement(String key, Double value) => Increment(key, -value);
    #endregion

    #region 日志
    /// <summary>日志</summary>
    public ILog Log { get; set; } = Logger.Null;

    /// <summary>客户端命令日志</summary>
    public ILog ClientLog { get; set; } = Logger.Null;

    /// <summary>写日志</summary>
    /// <param name="format"></param>
    /// <param name="args"></param>
    public void WriteLog(String format, params Object[] args) => Log?.Info(format, args);
    #endregion
}