using IGP.UnitySDK.Models;
using IGP.UnitySDK;
using System.Collections.Generic;
using System.Globalization;
using UnityEngine;

namespace IGP.Multiplayer.Core
{
    /// <summary>
    /// RTT (Round-Trip Time) 统计信息
    /// 用于测量和监控网络延迟
    /// </summary>
    public class IGPRTTStats
    {
        private readonly List<float> samples = new List<float>();
        private const int MaxSamples = 100; // 保留最近100个样本

        private float cumulativeSum = 0f;

        /// <summary>
        /// 最小RTT（秒）
        /// </summary>
        public float MinRTT { get; private set; } = float.MaxValue;

        /// <summary>
        /// 最大RTT（秒）
        /// </summary>
        public float MaxRTT { get; private set; } = 0f;

        /// <summary>
        /// 平均RTT（秒）
        /// </summary>
        public float AvgRTT { get; private set; } = 0f;

        /// <summary>
        /// 最后一次RTT（秒）
        /// </summary>
        public float LastRTT { get; private set; } = 0f;

        /// <summary>
        /// 样本数量
        /// </summary>
        public int SampleCount => samples.Count;

        /// <summary>
        /// 添加RTT样本
        /// </summary>
        /// <param name="rtt">RTT值（秒）</param>
        public void AddSample(float rtt)
        {
            LastRTT = rtt;
            MinRTT = Mathf.Min(MinRTT, rtt);
            MaxRTT = Mathf.Max(MaxRTT, rtt);

            samples.Add(rtt);
            cumulativeSum += rtt;

            // 保持样本数量在限制内
            if (samples.Count > MaxSamples)
            {
                var removed = samples[0];
                cumulativeSum -= removed;
                samples.RemoveAt(0);
            }

            AvgRTT = cumulativeSum / samples.Count;
        }

        /// <summary>
        /// 重置所有统计信息
        /// </summary>
        public void Reset()
        {
            samples.Clear();
            cumulativeSum = 0f;
            MinRTT = float.MaxValue;
            MaxRTT = 0f;
            AvgRTT = 0f;
            LastRTT = 0f;
        }
        
        /// <summary>
        /// 获取统计信息摘要
        /// </summary>
        /// <returns>(min, max, avg, last, count)</returns>
        public (float min, float max, float avg, float last, int count) GetStats()
        {
            return (MinRTT, MaxRTT, AvgRTT, LastRTT, SampleCount);
        }
        
        /// <summary>
        /// 获取网络质量评级
        /// </summary>
        /// <returns>优秀/良好/一般/较差/很差</returns>
        public string GetQuality()
        {
            float avgMs = AvgRTT * 1000f;
            
            if (avgMs < 20f) return "优秀";
            if (avgMs < 50f) return "良好";
            if (avgMs < 100f) return "一般";
            if (avgMs < 200f) return "较差";
            return "很差";
        }
        
        /// <summary>
        /// 获取质量颜色
        /// </summary>
        /// <returns>Unity Color</returns>
        public Color GetQualityColor()
        {
            float avgMs = AvgRTT * 1000f;
            
            if (avgMs < 20f) return Color.green;        // 优秀
            if (avgMs < 50f) return Color.yellow;       // 良好
            if (avgMs < 100f) return new Color(1f, 0.5f, 0f); // 一般（橙色）
            if (avgMs < 200f) return new Color(1f, 0.3f, 0f); // 较差（深橙）
            return Color.red;                            // 很差
        }
        
        /// <summary>
        /// 转换为字符串（用于日志）
        /// </summary>
        public override string ToString()
        {
            if (SampleCount == 0)
            {
                return "RTT: No samples";
            }

            return $"RTT: last={LastRTT*1000:F1}ms, avg={AvgRTT*1000:F1}ms, " +
                   $"min={MinRTT*1000:F1}ms, max={MaxRTT*1000:F1}ms, samples={SampleCount}";
        }
    }

    /// <summary>
    /// Snapshot of the underlying KCP transport state. Values come from KCP ACK timing
    /// and queues, so they are independent from the app-level ping/pong RTT samples.
    /// </summary>
    public readonly struct IGPKcpTransportStats
    {
        public static IGPKcpTransportStats Unavailable => default;

        public IGPKcpTransportStats(
            bool isAvailable,
            int smoothedRttMs,
            int rtoMs,
            int rttVarMs,
            int waitSnd,
            int sndQueue,
            int sndBuf,
            int rcvQueue,
            int rcvBuf,
            int ackList,
            uint cwnd,
            uint remoteWindow,
            long datagramsIn = 0,
            long datagramsOut = 0,
            float datagramsInPerSecond = 0f,
            float datagramsOutPerSecond = 0f,
            long datagramBytesIn = 0,
            long datagramBytesOut = 0)
            : this(
                isAvailable,
                smoothedRttMs,
                rtoMs,
                rttVarMs,
                waitSnd,
                sndQueue,
                sndBuf,
                rcvQueue,
                rcvBuf,
                ackList,
                cwnd,
                remoteWindow,
                datagramsIn,
                datagramsOut,
                datagramsInPerSecond,
                datagramsOutPerSecond,
                datagramBytesIn,
                datagramBytesOut,
                0,
                0,
                false,
                0,
                0,
                0,
                0)
        {
        }

        internal IGPKcpTransportStats(
            bool isAvailable,
            int smoothedRttMs,
            int rtoMs,
            int rttVarMs,
            int waitSnd,
            int sndQueue,
            int sndBuf,
            int rcvQueue,
            int rcvBuf,
            int ackList,
            uint cwnd,
            uint remoteWindow,
            long datagramsIn,
            long datagramsOut,
            float datagramsInPerSecond,
            float datagramsOutPerSecond,
            long datagramBytesIn,
            long datagramBytesOut,
            int pendingSendGroups,
            int pendingSendBytes,
            bool sendBackpressured,
            double sendProgressAgeSeconds,
            double receiveProgressAgeSeconds,
            long rejectedSendGroups,
            double oldestPendingSendAgeSeconds)
        {
            IsAvailable = isAvailable;
            SmoothedRttMs = smoothedRttMs;
            RtoMs = rtoMs;
            RttVarMs = rttVarMs;
            WaitSnd = waitSnd;
            SndQueue = sndQueue;
            SndBuf = sndBuf;
            RcvQueue = rcvQueue;
            RcvBuf = rcvBuf;
            AckList = ackList;
            Cwnd = cwnd;
            RemoteWindow = remoteWindow;
            DatagramsIn = datagramsIn;
            DatagramsOut = datagramsOut;
            DatagramsInPerSecond = datagramsInPerSecond;
            DatagramsOutPerSecond = datagramsOutPerSecond;
            DatagramBytesIn = datagramBytesIn;
            DatagramBytesOut = datagramBytesOut;
            PendingSendGroups = pendingSendGroups;
            PendingSendBytes = pendingSendBytes;
            SendBackpressured = sendBackpressured;
            SendProgressAgeSeconds = sendProgressAgeSeconds;
            ReceiveProgressAgeSeconds = receiveProgressAgeSeconds;
            RejectedSendGroups = rejectedSendGroups;
            OldestPendingSendAgeSeconds = oldestPendingSendAgeSeconds;
        }

        public bool IsAvailable { get; }
        public int SmoothedRttMs { get; }
        public int RtoMs { get; }
        public int RttVarMs { get; }
        public int WaitSnd { get; }
        public int SndQueue { get; }
        public int SndBuf { get; }
        public int RcvQueue { get; }
        public int RcvBuf { get; }
        public int AckList { get; }
        public uint Cwnd { get; }
        public uint RemoteWindow { get; }
        public long DatagramsIn { get; }
        public long DatagramsOut { get; }
        public float DatagramsInPerSecond { get; }
        public float DatagramsOutPerSecond { get; }
        public long DatagramBytesIn { get; }
        public long DatagramBytesOut { get; }
        public int PendingSendGroups { get; }
        public int PendingSendBytes { get; }
        public bool SendBackpressured { get; }
        public double SendProgressAgeSeconds { get; }
        public double ReceiveProgressAgeSeconds { get; }
        public long RejectedSendGroups { get; }
        public double OldestPendingSendAgeSeconds { get; }

        public bool HasSmoothedRtt => IsAvailable && SmoothedRttMs > 0;
    }

    internal static class IGPRTTDisplayFormatting
    {
        internal static string FormatUdpDatagramCount(long value)
        {
            if (value >= 999_500_000L)
            {
                return (value / 1_000_000_000d).ToString("0.#", CultureInfo.InvariantCulture) + "b";
            }

            if (value >= 999_500L)
            {
                return (value / 1_000_000d).ToString("0.#", CultureInfo.InvariantCulture) + "m";
            }

            if (value >= 10_000L)
            {
                return (value / 1_000d).ToString("0.#", CultureInfo.InvariantCulture) + "k";
            }

            return value.ToString("N0", CultureInfo.InvariantCulture);
        }

        internal static string FormatByteCount(long value)
        {
            if (value >= 1_099_511_627_776L)
            {
                return FormatByteUnit(value, 1_099_511_627_776d, "TB");
            }

            if (value >= 1_073_741_824L)
            {
                return FormatByteUnit(value, 1_073_741_824d, "GB");
            }

            if (value >= 1_048_576L)
            {
                return FormatByteUnit(value, 1_048_576d, "MB");
            }

            if (value >= 1024L)
            {
                return FormatByteUnit(value, 1024d, "KB");
            }

            return value.ToString("N0", CultureInfo.InvariantCulture) + " B";
        }

        private static string FormatByteUnit(long value, double unitBytes, string unit)
        {
            return (value / unitBytes).ToString("0.#", CultureInfo.InvariantCulture) + " " + unit;
        }
    }
}
