using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.Identity.Client;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

using StackExchange.Redis;

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

using IDatabase = StackExchange.Redis.IDatabase;
namespace HuanMeng.DotNetCore.Redis
{
    /// <summary>
    /// 数据库连接字符串
    /// </summary>
    public static class RedisConnection
    {
        /// <summary>
        /// 数据库连接
        /// </summary>
        public static ConcurrentDictionary<string, IDatabase> Redis { get; set; } = new ConcurrentDictionary<string, IDatabase>();

        /// <summary>
        /// 数据库查询
        /// </summary>
        public static ConcurrentDictionary<string, IServer> RedisServer { get; set; } = new ConcurrentDictionary<string, IServer>();


        /// <summary>
        /// 
        /// </summary>
        /// <param name="redisConnection"></param>
        /// <returns></returns>
        public static IDatabase GetRedis(string redisConnection)
        {
            if (!Redis.TryGetValue(redisConnection, out var database))
            {
                var redis = ConnectionMultiplexer.Connect(redisConnection);
                database = redis.GetDatabase();
                //server.key
                //redis.GetServer()
                Redis.TryAdd(redisConnection, database);
            }
            return database;
        }



        /// <summary>
        /// 
        /// </summary>
        /// <param name="redisConnection"></param>
        /// <returns></returns>
        public static IServer GetServer(string redisConnection)
        {
            if (!RedisServer.TryGetValue(redisConnection, out var server))
            {
                var redis = ConnectionMultiplexer.Connect(redisConnection);
                var serverConn = ParseIpPortAndDatabase(redisConnection);
                server = redis.GetServer(serverConn.ip, serverConn.port);
                //server.key
                //redis.GetServer()
                RedisServer.TryAdd(redisConnection, server);
            }
            return server;
        }
        private static (string ip, int port, int database) ParseIpPortAndDatabase(string connectionString)
        {
            // 默认端口号和默认数据库
            int defaultPort = 6379;
            int defaultDatabase = 0;

            if (string.IsNullOrEmpty(connectionString))
            {
                return ("localhost", defaultPort, defaultDatabase);
            }

            // 按逗号分割，获取主机部分和其他参数
            var parts = connectionString.Split(',');
            var hostPart = parts[0]; // 例如："192.168.195.6:6379"

            // 检查是否包含端口号
            string ip;
            int port;

            if (hostPart.Contains(":"))
            {
                var hostParts = hostPart.Split(':');
                ip = hostParts[0];
                port = int.TryParse(hostParts[1], out int parsedPort) ? parsedPort : defaultPort;
            }
            else
            {
                // 如果没有端口号，使用默认端口
                ip = hostPart;
                port = defaultPort;
            }

            // 解析 defaultDatabase 参数
            int database = defaultDatabase;
            foreach (var part in parts)
            {
                if (part.StartsWith("defaultDatabase=", StringComparison.OrdinalIgnoreCase))
                {
                    var dbPart = part.Split('=');
                    if (dbPart.Length == 2 && int.TryParse(dbPart[1], out int parsedDb))
                    {
                        database = parsedDb;
                    }
                }
            }

            return (ip, port, database);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="database"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="time"></param>
        /// <returns></returns>
        public static bool StringSetLock(this IDatabase database, string key, string value, int time = 10)
        {
            return database.StringSet(key, value, TimeSpan.FromSeconds(time), when: When.NotExists);
        }

        /// <summary>
        /// 删除key
        /// </summary>
        /// <param name="database"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public static async Task<int> KeysDeleteds(this IDatabase database, string key)
        {

            string luaScript = @"
local keys = redis.call('KEYS', ARGV[1])
if next(keys) ~= nil then
    return redis.call('DEL', unpack(keys))
else
    return 0
end
";

            var keysDeleted = (int)await (database.ScriptEvaluateAsync(luaScript, values: new RedisValue[] { key }));
            return keysDeleted;
        }


        /// <summary>
        /// 获取一个key的对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static T? StringGet<T>(this IDatabase database, string key)
        {
            var value = database.StringGet(key);
            // 检查值是否为空
            if (!value.HasValue)
            {
                return default(T);
            }
            if (typeof(T).IsPrimitive || typeof(T) == typeof(string) || typeof(T) == typeof(decimal))
            {
                try
                {
                    return (T)Convert.ChangeType(value.ToString(), typeof(T));
                }
                catch
                {
                    return default; // 或抛出异常，取决于业务需求
                }
            }
            // 将 RedisValue 转换为 T 类型
            return JsonConvert.DeserializeObject<T>(value);
        }

        /// <summary>
        /// 数据存放在redis
        /// </summary>
        /// <param name="database"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public static async Task<bool> StringSetAsync(this IDatabase database, string key, object value, TimeSpan? expiry)
        {
            return await database.StringSetAsync(key, (value == null ? "" : JsonConvert.SerializeObject(value)), expiry, When.Always);
        }

        /// <summary>
        /// 数据存放在redis
        /// </summary>
        /// <param name="database"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="expiry"></param>
        /// <returns></returns>
        public static bool StringSet(this IDatabase database, string key, object value, TimeSpan? expiry = null)
        {
            return database.StringSet(key, (value == null ? "" : JsonConvert.SerializeObject(value)), expiry, When.Always);
        }

        /// <summary>
        /// 获取一个key的对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public static async Task<T?> StringGetAsync<T>(this IDatabase database, string key)
        {

            var value = await database.StringGetAsync(key);
            // 检查值是否为空
            if (!value.HasValue)
            {
                return default(T);
            }
            if (typeof(T).IsPrimitive || typeof(T) == typeof(string) || typeof(T) == typeof(decimal))
            {
                try
                {
                    return (T)Convert.ChangeType(value.ToString(), typeof(T));
                }
                catch
                {
                    return default; // 或抛出异常，取决于业务需求
                }
            }
            // 将 RedisValue 转换为 T 类型
            return JsonConvert.DeserializeObject<T>(value);
        }


        /// <summary>
        /// 模糊查询key
        /// </summary>
        /// <param name="server"></param>
        /// <param name="pattern"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public static List<string> ScanKeys(this IServer server, string pattern, int pageSize = 100)
        {
            var matchingKeys = server.Keys(pattern: pattern, pageSize: pageSize).Select(it => it.ToString()).ToList();
            return matchingKeys;
        }
        /// <summary>
        /// 异步模糊查询key
        /// </summary>
        /// <param name="server">Redis 服务器实例</param>
        /// <param name="pattern">匹配的模式（支持通配符）</param>
        /// <param name="pageSize">每次扫描的页大小</param>
        /// <param name="database">数据库编号，默认值为 -1 表示全部数据库</param>
        /// <returns>匹配的键的字符串列表</returns>
        public static async Task<List<string>> ScanKeysAsync(this IServer server, string pattern, int pageSize = 100, int database = -1)
        {
            var matchingKeys = new List<string>();

            try
            {
                await foreach (var key in server.KeysAsync(database: database, pattern: pattern, pageSize: pageSize))
                {
                    matchingKeys.Add(key.ToString());
                }
            }
            catch (Exception ex)
            {
                // 可以记录日志或者处理异常
                Console.WriteLine($"Error while scanning keys: {ex.Message}");
            }

            return matchingKeys;
        }


        /// <summary>
        /// 模糊查询所有匹配的键的数量
        /// </summary>
        /// <param name="server">Redis 服务器实例</param>
        /// <param name="pattern">匹配的模式（支持通配符）</param>
        /// <returns>匹配的键的数量</returns>
        public static int ScanKeysCount(this IServer server, string pattern, int database = -1)
        {
            int count = 0;
            long cursor = 0; // 游标，用于记录扫描进度

            do
            {
                // 使用 Keys 方法进行分页查询
                var keys = server.Keys(database: database, cursor: cursor, pattern: pattern, pageSize: 1000).ToArray();

                // 累计匹配到的键的数量
                count += keys.Length;

                // 如果还有更多键，则获取新的游标；否则，循环终止
                cursor = keys.Length == 1000 ? 1 : 0;

            } while (cursor != 0);

            return count;
        }

        /// <summary>
        /// 异步模糊查询所有匹配的键的数量
        /// </summary>
        /// <param name="server">Redis 服务器实例</param>
        /// <param name="pattern">匹配的模式（支持通配符）</param>
        /// <param name="pageSize">每次扫描的页大小</param>
        /// <param name="database">数据库编号，默认为 -1 表示当前数据库</param>
        /// <returns>匹配的键的数量</returns>
        public static async Task<int> ScanKeysCountAsync(this IServer server, string pattern, int pageSize = 1000, int database = -1)
        {
            int count = 0;

            // 异步遍历 KeysAsync
            await foreach (var key in server.KeysAsync(database: database, pattern: pattern, pageSize: pageSize))
            {
                count++;
            }

            return count;
        }


    }
}
