using dodohold.core;
using System.Net;


namespace molilian.core
{
    public partial class ProxyNodesCore
    {


        private static string _end_point;
        static ProxyNodesCore()
        {
            _end_point = Environment.GetEnvironmentVariable("EndPoint");
        }


        private static readonly object _lockObj = new();
        private static IEnumerable<ProxyNodesDTO> _cached;
        private static IEnumerable<ProxyNodesDTO> _all_cached;
        public static string GetSelectOne(string node, bool all_node = false)
        {
            var list = all_node ? AllList() : List();
            if (!list.Any()) return null;

            var nodes = SplitNodeNames(node).ToArray();
            if (nodes.Length == 0) return node?.Trim();
            if (nodes.Length == 1) return nodes[0];

            // 处理包含逗号的节点参数
            string selectedNode = node;
            var random = new Random();
            selectedNode = nodes[random.Next(nodes.Length)];
            return selectedNode;
        }


        public static WebProxy? GetOne(string node, bool all_node = false)
        {
            var list = all_node ? AllList() : List();
            if (!list.Any()) return null;

            // 处理包含逗号的节点参数
            string selectedNode = node;
            var nodes = SplitNodeNames(node).ToArray();
            if (nodes.Length > 1)
            {
                var random = new Random();
                selectedNode = nodes[random.Next(nodes.Length)];
            }
            else if (nodes.Length == 1)
            {
                selectedNode = nodes[0];
            }

            var ProxyNodesDTO = list.Where(e => SelectEndPointUseProxy(e, selectedNode))
                                   .OrderBy(l => Guid.NewGuid())
                                   .FirstOrDefault();
            if (ProxyNodesDTO == null) return null;


            return new WebProxy
            {
                Address = new Uri(ProxyNodesDTO.server),
                Credentials = new NetworkCredential(ProxyNodesDTO.username, ProxyNodesDTO.password)
            };
        }





        public static WebProxy? GetOne(int nodeid)
        {
            var list = List();
            if (!list.Any()) return null;

            var ProxyNodesDTO = list.Where(e => SelectEndPointUseProxy(e, nodeid)).OrderBy(l => Guid.NewGuid()).FirstOrDefault();
            if (ProxyNodesDTO == null) return null;

            return new WebProxy
            {
                Address = new Uri(ProxyNodesDTO.server),
                Credentials = new NetworkCredential(ProxyNodesDTO.username, ProxyNodesDTO.password)
            };
        }


        private static bool SelectEndPointUseProxy(ProxyNodesDTO item, string node = "")
        {
            if (!string.IsNullOrEmpty(_end_point) && !string.IsNullOrEmpty(item.end_point))
            {
                if (item.end_point != _end_point) return false;
            }
            return string.IsNullOrEmpty(node) || IsMatchNode(item, node);
        }
        private static bool SelectEndPointUseProxy(ProxyNodesDTO item, int nodeid)
        {
            if (!string.IsNullOrEmpty(_end_point) && !string.IsNullOrEmpty(item.end_point))
            {
                if (item.end_point != _end_point) return false;
            }
            return nodeid <= 0 || item.id == nodeid;
        }

        public static WebProxy? RandomOne(string excludeNode = "")
        {
#if DEBUG
            return null;
#endif

            var list = List();
            if (!list.Any()) return null;
            var ProxyNodesDTO = list.Where(e => SelectEndPointExcludeNodeUseProxy(e, string.Empty)).OrderBy(l => Guid.NewGuid()).FirstOrDefault();
            if (ProxyNodesDTO == null) return null;

            return new WebProxy
            {
                Address = new Uri(ProxyNodesDTO.server),
                Credentials = new NetworkCredential(ProxyNodesDTO.username, ProxyNodesDTO.password)
            };
        }
        private static bool SelectEndPointExcludeNodeUseProxy(ProxyNodesDTO item, string excludeNode = "")
        {
            if (!string.IsNullOrEmpty(_end_point) && !string.IsNullOrEmpty(item.end_point))
            {
                if (item.end_point != _end_point) return false;
            }
            return string.IsNullOrEmpty(excludeNode) || !IsMatchNode(item, excludeNode);
        }

        public static IEnumerable<string> SplitNodeNames(string nodeNames)
        {
            if (string.IsNullOrWhiteSpace(nodeNames))
            {
                return Array.Empty<string>();
            }

            return nodeNames
                .Replace('，', ',')
                .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
                .Where(item => !string.IsNullOrWhiteSpace(item));
        }

        public static bool IsMatchNode(ProxyNodesDTO item, string nodeName)
        {
            if (string.IsNullOrWhiteSpace(nodeName))
            {
                return false;
            }

            var configuredNodes = SplitNodeNames(nodeName)
                .ToHashSet(StringComparer.OrdinalIgnoreCase);
            if (configuredNodes.Count == 0)
            {
                return false;
            }

            return new[] { item.nodeName, item.name }
                .Where(value => !string.IsNullOrWhiteSpace(value))
                .Select(value => value.Trim())
                .Any(value => configuredNodes.Contains(value));
        }

        public static IEnumerable<ProxyNodesDTO> List(bool force = false)
        {
            if (!force && _cached != null) return _cached;

            string cache_key = $"cache:proxy_nodes";
            var list = RedisHelper.Get<IEnumerable<ProxyNodesDTO>>(cache_key);
            if (!force && NeedReloadNodeNameCache(list))
            {
                list = null;
            }
            if (force || list == null)
            {
                lock (_lockObj)
                {
                    list = new DBContext.Table("proxy_nodes")
                        .Where("status=@status", new { status = 1 })
                        .Select<ProxyNodesDTO>();
                    if (list == null) return default;
                    RedisHelper.Set(cache_key, list, 30 * 86400);
                }
            }
            _cached = list;
            return list;
        }

        public static IEnumerable<ProxyNodesDTO> AllList(bool force = false)
        {
            if (!force && _all_cached != null) return _all_cached;

            string cache_key = $"cache:all_proxy_nodes";
            var list = RedisHelper.Get<IEnumerable<ProxyNodesDTO>>(cache_key);
            if (!force && NeedReloadNodeNameCache(list))
            {
                list = null;
            }
            if (force || list == null)
            {
                lock (_lockObj)
                {
                    list = new DBContext.Table("proxy_nodes")
                        .Where("", new { status = 1 })
                        .Select<ProxyNodesDTO>();
                    if (list == null) return default;
                    RedisHelper.Set(cache_key, list, 30 * 86400);
                }
            }
            _all_cached = list;
            return list;
        }
        public static void Refresh()
        {
            _ = List(true);
            _ = AllList(true);
        }

        private static bool NeedReloadNodeNameCache(IEnumerable<ProxyNodesDTO> list)
        {
            if (list == null)
            {
                return false;
            }

            var items = list.ToArray();
            return items.Length > 0 && items.All(item => string.IsNullOrWhiteSpace(item.nodeName));
        }

    }

}
