using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Ae.Dns.Protocol.Enums;
using Ae.Dns.Protocol.Records;
using PCL.Core.IO.Net.Dns;
using PCL.Core.Logging;
using PCL.Core.Utils;

namespace PCL.Core.Minecraft;

public static class ServerAddressResolver
{
    public readonly record struct ResolvedServerAddress(string Host, string? Ip, int Port);

    // Minecraft Java 默认端口
    private const int DefaultPort = 25565;
    // Happy Eyeballs 族间启动间隔（降低首包时延）
    private static readonly TimeSpan _HappyEyeballsStagger = TimeSpan.FromMilliseconds(250);
    // 单次 TCP 连接超时
    private static readonly TimeSpan _ConnectTimeout = TimeSpan.FromSeconds(2.5);

    // [IPv6]:port 或 [IPv6]
    private static readonly Regex _BracketedIpv6 =
        new(@"^\[(?<ip>.+?)\](?::(?<port>\d{1,5}))?$", RegexOptions.Compiled | RegexOptions.CultureInvariant);

    // 纯端口（用于 host:port 末尾匹配）
    private static readonly Regex _TrailingPort =
        new(@":(?<port>\d{1,5})$", RegexOptions.Compiled | RegexOptions.CultureInvariant);

    public static async Task<ResolvedServerAddress> GetResolvedServerAddressAsync(string address, CancellationToken cancelToken = default)
    {
        if (string.IsNullOrWhiteSpace(address))
            throw new ArgumentException("服务器地址不能为空", nameof(address));

        // 规范化：去除 scheme、空白、尾随斜杠
        address = _NormalizeInput(address);

        // 1) 解析出 host/ip 与端口（若端口未提供则为 null）
        var (hostOrIp, portOpt) = _ParseHostAndPort(address);

        // 2) 显式端口 => 禁止 SRV，直接解析 IP 并尝试连接
        if (portOpt is { } explicitPort)
        {
            _ValidatePort(explicitPort);
            LogWrapper.Info($"使用显式端口，跳过 SRV：{hostOrIp}:{explicitPort}");
            var target = await _ResolveReachableAsync(hostOrIp, explicitPort, cancelToken).ConfigureAwait(false);
            if (target is not null)
                return new ResolvedServerAddress(hostOrIp, target.Value.Ip, target.Value.Port);

            // 回退策略：无法连接则仍返回解析到的首个 IP
            var fallbackIp = await _ResolveFirstIpAsync(hostOrIp, cancelToken).ConfigureAwait(false);
            return new ResolvedServerAddress(hostOrIp, fallbackIp, explicitPort);
        }

        // 3) 未指定端口
        // 3.1 纯 IP（IPv4/IPv6）=> 直接使用默认端口
        if (IPAddress.TryParse(hostOrIp, out _))
        {
            var target = await _ResolveReachableAsync(hostOrIp, DefaultPort, cancelToken).ConfigureAwait(false);
            if (target is not null)
                return new ResolvedServerAddress(hostOrIp, target.Value.Ip, target.Value.Port);

            return new ResolvedServerAddress(hostOrIp, hostOrIp, DefaultPort);
        }

        // 3.2 域名 => 先尝试 SRV（_minecraft._tcp.），成功则按 SRV 顺序与加权尝试
        var idnHost = _ToAsciiIdn(hostOrIp);
        var srvOrdered = await _QuerySrvOrderedAsync(idnHost, cancelToken).ConfigureAwait(false);

        if (srvOrdered.Count > 0)
        {
            LogWrapper.Info($"SRV 记录可用（{srvOrdered.Count}）: _minecraft._tcp.{idnHost}");
            foreach (var srv in srvOrdered)
            {
                var targetHost = _TrimTrailingDot(srv.Target);
                var port = srv.Port;

                var reachable = await _ResolveReachableAsync(targetHost, port, cancelToken).ConfigureAwait(false);
                if (reachable is not null)
                {
                    LogWrapper.Info($"SRV 命中：{targetHost}:{port} -> {reachable.Value.Ip}:{port}");
                    return new ResolvedServerAddress(idnHost, reachable.Value.Ip, reachable.Value.Port);
                }
            }

            // SRV 全部不可达则回退到 SRV 第一条的解析 IP 或域名默认端口
            var first = srvOrdered[0];
            var firstIp = await _ResolveFirstIpAsync(_TrimTrailingDot(first.Target), cancelToken).ConfigureAwait(false);
            if (!string.IsNullOrEmpty(firstIp)) return new ResolvedServerAddress(idnHost, firstIp, first.Port);
        }
        else
        {
            LogWrapper.Info($"无 SRV 记录或查询失败，回退默认端口：{idnHost}:{DefaultPort}");
        }

        // 3.3 最终回退：域名 + 默认端口
        var ip = await _ResolveFirstIpAsync(idnHost, cancelToken).ConfigureAwait(false);
        return new ResolvedServerAddress(idnHost, ip, DefaultPort);
    }

    // 规范化地址输入：去掉 scheme、空白、尾随 '/'
    private static string _NormalizeInput(string input)
    {
        var s = input.Trim();

        // 去掉任意 scheme:// 前缀（例如 http://、https://、minecraft://）
        var schemeIdx = s.IndexOf("://", StringComparison.Ordinal);
        if (schemeIdx > 0)
            s = s[(schemeIdx + 3)..];

        // 去掉尾随的 '/'
        while (s.EndsWith("/", StringComparison.Ordinal))
            s = s[..^1];

        return s;
    }

    private static void _ValidatePort(int port)
    {
        if (port is < 1 or > 65535)
            throw new FormatException($"无效的端口：{port}");
    }

    private static string _ToAsciiIdn(string host)
    {
        try
        {
            // 处理国际化域名
            var idn = new IdnMapping();
            // 允许末尾点号（FQDN）
            var h = _TrimTrailingDot(host);
            return idn.GetAscii(h) + (host.EndsWith(".", StringComparison.Ordinal) ? "." : "");
        }
        catch
        {
            // 无法转换时返回原值，交给后续 DNS 解析处理
            return host;
        }
    }

    private static string _TrimTrailingDot(string host)
        => host.EndsWith(".", StringComparison.Ordinal) ? host[..^1] : host;

    private static (string HostOrIp, int? Port) _ParseHostAndPort(string input)
    {
        // 1) [IPv6] 或 [IPv6]:port
        var m = _BracketedIpv6.Match(input);
        if (m.Success)
        {
            var ip = m.Groups["ip"].Value;
            if (!IPAddress.TryParse(ip, out _))
                throw new FormatException("无效的 IPv6 地址格式");
            var portGroup = m.Groups["port"];
            if (portGroup.Success)
            {
                var port = int.Parse(portGroup.Value, CultureInfo.InvariantCulture);
                _ValidatePort(port);
                return (ip, port);
            }
            return (ip, null);
        }

        // 2) 试图解析为纯 IP（IPv4 或 IPv6 无端口）
        if (IPAddress.TryParse(input, out _))
            return (input, null);

        // 3) host:port（仅在末尾存在且为纯数字端口时成立）
        var pm = _TrailingPort.Match(input);
        if (pm.Success)
        {
            // 防止误把 IPv6 当作 host:port（IPv6 必须用中括号携带端口）
            // 此处 input 中若包含多个 ':' 则极可能是 IPv6 而非 host:port
            var colonCount = input.Count(c => c == ':');
            if (colonCount == 1)
            {
                var port = int.Parse(pm.Groups["port"].Value, CultureInfo.InvariantCulture);
                _ValidatePort(port);
                var host = input[..^pm.Value.Length];
                if (string.IsNullOrWhiteSpace(host))
                    throw new FormatException("无效的主机名");
                return (host, port);
            }
        }

        // 4) 其余情况按“域名（无端口）”处理
        return (input, null);
    }

    // ===== SRV 查询与排序（RFC 2782） =====

    private sealed record SrvRecord(int Priority, int Weight, int Port, string Target);

    private static async Task<List<SrvRecord>> _QuerySrvOrderedAsync(string domain, CancellationToken ct)
    {
        try
        {
            var name = $"_minecraft._tcp.{_TrimTrailingDot(domain)}";
            LogWrapper.Info($"尝试 SRV 查询：{name}");

            // NDnsQuery.GetSrvRecords 返回 string 列表，为兼容不同实现，这里进行鲁棒解析
            var raw = await DnsQuery.Instance.QueryAsync(name, DnsQueryType.SRV, ct);
            if (raw is null || raw.Answers.Count == 0) return [];
            List<SrvRecord> parsed = [];
            foreach (var answer in raw.Answers)
            {
                if (answer.Resource is not DnsUnknownResource dnsRaw) return [];
                var srcRecord = new DnsSrvResource();
                var offset = 0;
                srcRecord.ReadBytes(dnsRaw.Raw, ref offset, dnsRaw.Raw.Length);
                parsed.Add(new SrvRecord(srcRecord.Priority, srcRecord.Weight, srcRecord.Port, srcRecord.Target));
            }

            // 过滤 target 为 "."（表示服务不可用）
            parsed.RemoveAll(p => p.Target == ".");

            if (parsed.Count == 0)
                return [];

            // RFC 2782：按 priority 升序；相同 priority 内按权重加权随机选择顺序
            var ordered = new List<SrvRecord>(parsed.Count);
            foreach (var group in parsed.GroupBy(p => p.Priority).OrderBy(g => g.Key))
            {
                var pool = group.ToList();
                while (pool.Count > 0)
                {
                    var next = _PopByWeight(pool);
                    ordered.Add(next);
                }
            }
            return ordered;
        }
        catch (SocketException ex)
        {
            LogWrapper.Warn(ex, "SRV 查询失败（网络错误）");
            return [];
        }
        catch (Exception ex)
        {
            LogWrapper.Warn(ex, "SRV 查询异常");
            return [];
        }
    }

    private static SrvRecord _PopByWeight(List<SrvRecord> pool)
    {
        // RFC 2782 加权随机：在组内以 weight 为权重抽取
        var total = pool.Sum(p => p.Weight);
        if (total <= 0)
        {
            // 无权重时等概率
            var i = RandomUtils.NextInt(0, pool.Count - 1);
            var chosen = pool[i];
            pool.RemoveAt(i);
            return chosen;
        }

        var r = RandomUtils.NextInt(1, total); // (1..total)
        var sum = 0;
        for (var i = 0; i < pool.Count; i++)
        {
            sum += pool[i].Weight;
            if (sum >= r)
            {
                var chosen = pool[i];
                pool.RemoveAt(i);
                return chosen;
            }
        }

        // 理论不可达，兜底返回末尾
        var last = pool[^1];
        pool.RemoveAt(pool.Count - 1);
        return last;
    }

    // ===== DNS 与连接可达性 =====

    private static async Task<(string Ip, int Port)?> _ResolveReachableAsync(string hostOrIp, int port, CancellationToken ct)
    {
        try
        {
            // 已是字面量 IP
            if (IPAddress.TryParse(hostOrIp, out var ipLiteral))
            {
                var result = await _ConnectOneAsync(ipLiteral, port, ct).ConfigureAwait(false);
                if (result.ok) return (ipLiteral.ToString(), port);
                return null;
            }

            var addresses = await Dns.GetHostAddressesAsync(_TrimTrailingDot(hostOrIp), ct).ConfigureAwait(false);
            if (addresses.Length == 0)
                return null;

            // Happy Eyeballs：分组（IPv6、IPv4），按组分阶段并行连接，取首个成功
            var v6 = addresses.Where(a => a.AddressFamily == AddressFamily.InterNetworkV6).ToArray();
            var v4 = addresses.Where(a => a.AddressFamily == AddressFamily.InterNetwork).ToArray();

            // 第一阶段：IPv6
            var winner = await _ConnectAnyAsync(v6, port, TimeSpan.Zero, ct).ConfigureAwait(false);
            if (winner is not null)
                return (winner, port);

            // 第二阶段：IPv4（稍作延迟以避免同时轰炸）
            winner = await _ConnectAnyAsync(v4, port, _HappyEyeballsStagger, ct).ConfigureAwait(false);
            if (winner is not null)
                return (winner, port);

            return null;
        }
        catch (Exception ex) when (ex is not OperationCanceledException)
        {
            LogWrapper.Warn(ex, $"解析或连接失败：{hostOrIp}:{port}");
            return null;
        }
    }

    private static async Task<string?> _ResolveFirstIpAsync(string hostOrIp, CancellationToken ct)
    {
        try
        {
            if (IPAddress.TryParse(hostOrIp, out var ip))
                return ip.ToString();

            var addresses = await Dns.GetHostAddressesAsync(_TrimTrailingDot(hostOrIp), ct).ConfigureAwait(false);
            var chosen = addresses.FirstOrDefault(a => a.AddressFamily == AddressFamily.InterNetworkV6)
                         ?? addresses.FirstOrDefault(a => a.AddressFamily == AddressFamily.InterNetwork);
            return chosen?.ToString();
        }
        catch (Exception ex) when (ex is not OperationCanceledException)
        {
            LogWrapper.Warn(ex, $"DNS 解析失败：{hostOrIp}");
            return null;
        }
    }

    private static async Task<string?> _ConnectAnyAsync(IReadOnlyList<IPAddress> addrs, int port, TimeSpan delay, CancellationToken ct)
    {
        if (addrs.Count == 0) return null;
        if (delay > TimeSpan.Zero)
            await Task.Delay(delay, ct).ConfigureAwait(false);

        using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
        var tasks = new List<Task<(bool ok, string ip)>>(addrs.Count);
        tasks.AddRange(addrs.Select(ip => _ConnectOneAsync(ip, port, cts.Token)));

        while (tasks.Count > 0)
        {
            var done = await Task.WhenAny(tasks).ConfigureAwait(false);
            tasks.Remove(done);
            var (ok, ip) = await done.ConfigureAwait(false);
            if (!ok) continue;
            // 取消其余连接尝试
            try { await cts.CancelAsync().ConfigureAwait(false); } catch { /* ignore */ }
            return ip;
        }

        return null;
    }

    private static async Task<(bool ok, string ip)> _ConnectOneAsync(IPAddress ip, int port, CancellationToken ct)
    {
        try
        {
            using var sock = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            sock.NoDelay = true;

            using var timeoutCts = new CancellationTokenSource(_ConnectTimeout);
            using var linked = CancellationTokenSource.CreateLinkedTokenSource(ct, timeoutCts.Token);
#if NET8_0_OR_GREATER
            await sock.ConnectAsync(new IPEndPoint(ip, port), linked.Token).ConfigureAwait(false);
#else
            await sock.ConnectAsync(new IPEndPoint(ip, port)).WaitAsync(ConnectTimeout, linked.Token).ConfigureAwait(false);
#endif
            return (true, ip.ToString());
        }
        catch
        {
            return (false, ip.ToString());
        }
    }
}
