using System.Collections.Concurrent;
using System.Net;
using System.Net.NetworkInformation; // 添加这个引用
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Text;
using Common;
using DotNext;
using DotNext.Threading;
using Newtonsoft.Json;
using WebProtocol;

/// <summary> UDP接受数据包格式 </summary>
public class UDPData
{
    /// <summary>
    /// 接受到的时间
    /// </summary>
    public required DateTime DateTime { get; set; }
    /// <summary>
    /// 数据包时间戳
    /// </summary>
    public required UInt32 Timestamp { get; set; }
    /// <summary>
    /// 发送来源的IP地址
    /// </summary>
    public required string Address { get; set; }
    /// <summary>
    /// 发送来源的端口号
    /// </summary>
    public required int Port { get; set; }

    /// <summary>
    /// 任务ID
    /// </summary>
    public required int TaskID { get; set; }
    /// <summary>
    /// 接受到的数据
    /// </summary>
    public required byte[] Data { get; set; }
    /// <summary>
    /// 是否被读取过
    /// </summary>
    public required bool HasRead { get; set; }

    /// <summary>
    /// 深度拷贝对象
    /// </summary>
    /// <returns>UDPData</returns>
    public UDPData DeepClone()
    {
        var cloneData = new byte[this.Data.Length];
        Buffer.BlockCopy(this.Data, 0, cloneData, 0, this.Data.Length);
        return new UDPData()
        {
            DateTime = this.DateTime,
            Timestamp = this.Timestamp,
            Address = new string(this.Address),
            Port = this.Port,
            TaskID = this.TaskID,
            Data = cloneData,
            HasRead = this.HasRead
        };
    }

    /// <summary>
    /// 将UDP Data 转化为Json 格式字符串
    /// </summary>
    /// <returns>json字符串</returns>
    public override string ToString()
    {
        return JsonConvert.SerializeObject(this);
    }
}

/// <summary>
/// UDP 服务器
/// </summary>
public class UDPServer
{
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

    private ConcurrentDictionary<string, SortedList<UInt32, UDPData>> udpData
        = new ConcurrentDictionary<string, SortedList<UInt32, UDPData>>();
    private readonly AsyncReaderWriterLock udpDataLock = new AsyncReaderWriterLock();

    private int listenPort;
    private List<UdpClient> listeners = new List<UdpClient>();
    private List<Task> tasks = new List<Task>();
    private IPEndPoint groupEP;

    private CancellationTokenSource? cancellationTokenSource;
    private bool disposed = false;

    /// <summary>
    /// 是否正在工作
    /// </summary>
    public bool IsRunning => cancellationTokenSource?.Token.IsCancellationRequested == false;

    /// <summary> UDP 服务器的错误代码 </summary>
    public enum ErrorCode
    {
        /// <summary> [TODO:description] </summary>
        Success = 0,
        /// <summary> [TODO:description] </summary>
        GetNoneAfterTimeout,
        /// <summary> [TODO:description] </summary>
        ResponseWrong,
        /// <summary> [TODO:description] </summary>
        NotRecvDataPackage,
    }

    /// <summary>
    /// Construct a udp server with fixed port
    /// </summary>
    /// <param name="port"> Device UDP Port </param>
    /// <param name="num"> UDP Client Num </param>
    /// <returns> UDPServer class </returns>
    public UDPServer(int port, int num)
    {
        // Construction
        this.listenPort = port;
        try
        {
            for (int i = 0; i < num; i++)
            {
                int currentPort = this.listenPort + i;
                if (IsPortInUse(currentPort))
                {
                    throw new ArgumentException(
                        $"端口{currentPort}已被占用，无法启动UDP Server",
                        nameof(port)
                    );
                }
                listeners.Add(new UdpClient(currentPort));
            }
            this.groupEP = new IPEndPoint(IPAddress.Any, listenPort);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
            throw new ArgumentException(
                $"Failed to set up server with this port: {port}",
                nameof(port)
                );
        }
    }

    private bool IsPortInUse(int port)
    {
        bool inUse = false;
        try
        {
            var ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
            var udpListeners = ipGlobalProperties.GetActiveUdpListeners();
            foreach (var ep in udpListeners)
            {
                if (ep.Port == port)
                {
                    inUse = true;
                    break;
                }
            }
        }
        catch (Exception ex)
        {
            logger.Warn($"Failed to check port usage for port {port}: {ex.Message}");
        }
        return inUse;
    }

    /// <summary>
    /// 异步寻找目标发送的内容
    /// </summary>
    /// <param name="ipAddr"> 目标IP地址 </param>
    /// <param name="taskID">[TODO:parameter]</param>
    /// <param name="timeout">超时时间</param>
    /// <param name="cycle">延迟时间</param>
    /// <param name="callerName">调用函数名称</param>
    /// <param name="callerLineNum">调用函数位置</param>
    /// <returns>
    /// 异步Optional 数据包：
    /// Optional 为空时，表明找不到数据；
    /// Optional 存在时，为最先收到的数据
    /// </returns>
    public async ValueTask<Optional<UDPData>> FindDataAsync(
         string ipAddr, int taskID, int timeout = 1000, int cycle = 0,
         [CallerMemberName] string callerName = "",
         [CallerLineNumber] int callerLineNum = 0
         )
    {
        UDPData? data = null;
        var key = $"{ipAddr}-{taskID}";

        var startTime = DateTime.Now;
        var isTimeout = false;
        while (!isTimeout)
        {
            var elapsed = DateTime.Now - startTime;
            isTimeout = elapsed >= TimeSpan.FromMilliseconds(timeout);
            if (isTimeout) break;

            try
            {
                using (await udpDataLock.AcquireWriteLockAsync(TimeSpan.FromMilliseconds(timeout)))
                {
                    if (udpData.TryGetValue(key, out var sortedList) && sortedList.Count > 0)
                    {
                        // 获取最早的数据（第一个元素）
                        var firstKey = sortedList.Keys[0];
                        data = sortedList[firstKey];
                        sortedList.RemoveAt(0);
                        break;
                    }
                }
            }
            catch
            {
                logger.Trace("Get nothing even after time out");
                return new(null);
            }
        }


        if (data is null)
        {
            logger.Trace("Get nothing even after time out");
            return new(null);
        }
        else
        {
            return new(data.DeepClone());
        }
    }

    /// <summary>
    /// 异步寻找目标发送的所有内容，并清空队列
    /// </summary>
    /// <param name="ipAddr">目标IP地址</param>
    /// <param name="taskID">任务ID</param>
    /// <param name="timeout">超时时间</param>
    /// <returns>异步Optional 数据包列表</returns>
    public async ValueTask<Optional<List<UDPData>>> FindDataArrayAsync(string ipAddr, int taskID, int timeout = 1000)
    {
        List<UDPData>? data = null;
        var key = $"{ipAddr}-{taskID}";


        var startTime = DateTime.Now;
        var isTimeout = false;
        while (!isTimeout)
        {
            var elapsed = DateTime.Now - startTime;
            isTimeout = elapsed >= TimeSpan.FromMilliseconds(timeout);
            if (isTimeout) break;

            try
            {
                using (await udpDataLock.AcquireWriteLockAsync(TimeSpan.FromMilliseconds(timeout)))
                {
                    if (udpData.TryGetValue(key, out var sortedList) && sortedList.Count > 0)
                    {
                        data = new List<UDPData>(sortedList.Values);
                        // 输出数据
                        // PrintDataArray(data);
                        sortedList.Clear();
                        break;
                    }
                }
            }
            catch
            {
                logger.Trace("Get nothing even after time out");
                return new(null);
            }
        }

        if (data is null)
        {
            logger.Trace("Get nothing even after time out");
            return new(null);
        }
        else
        {
            return new(data);
        }
    }

    /// <summary>
    /// 获取还未被读取的数据列表
    /// </summary>
    /// <param name="ipAddr">IP地址</param>
    /// <param name="taskID">任务ID</param>
    /// <param name="timeout">超时时间</param>
    /// <returns>数据列表</returns>
    public async ValueTask<Optional<List<UDPData>>> GetDataArrayAsync(string ipAddr, int taskID, int timeout = 1000)
    {
        List<UDPData>? data = null;

        try
        {
            using (await udpDataLock.AcquireReadLockAsync(TimeSpan.FromMilliseconds(timeout)))
            {
                var key = $"{ipAddr}-{taskID}";
                if (udpData.TryGetValue(key, out var sortedList) && sortedList.Count > 0)
                {
                    data = new List<UDPData>(sortedList.Values);
                }
            }
        }
        catch (TimeoutException)
        {
            logger.Trace("Failed to acquire read lock within timeout");
            return new(null);
        }

        if (data is null)
        {
            logger.Trace("Get nothing even after time out");
            return new(null);
        }
        else
        {
            return new(data);
        }
    }

    /// <summary>
    /// 异步获取指定IP和任务ID的数据队列长度
    /// </summary>
    /// <param name="ipAddr">IP地址</param>
    /// <param name="taskID">任务ID</param>
    /// <param name="timeout">超时时间</param>
    /// <returns>数据队列长度</returns>
    public async ValueTask<Optional<int>> GetDataCountAsync(string ipAddr, int taskID, int timeout = 1000)
    {
        int? count = null;

        try
        {
            using (await udpDataLock.AcquireReadLockAsync(TimeSpan.FromMilliseconds(timeout)))
            {
                var key = $"{ipAddr}-{taskID}";
                if (udpData.TryGetValue(key, out var sortedList))
                {
                    count = sortedList.Count;
                }
            }
        }
        catch (TimeoutException)
        {
            logger.Trace("Failed to acquire read lock within timeout");
            return Optional<int>.None;
        }

        if (count is null)
        {
            logger.Trace("Get nothing even after time out");
            return Optional<int>.None;
        }
        else
        {
            return new(count.Value);
        }
    }

    /// <summary>
    /// 异步等待写响应
    /// </summary>
    /// <param name="address">IP地址</param>
    /// <param name="taskID">[TODO:parameter]</param>
    /// <param name="port">UDP 端口</param>
    /// <param name="timeout">超时时间范围</param>
    /// <returns>接收响应包</returns>
    public async ValueTask<Result<WebProtocol.RecvRespPackage>> WaitForAckAsync
        (string address, int taskID, int port = -1, int timeout = 1000)
    {
        var data = await FindDataAsync(address, taskID, timeout);
        if (!data.HasValue)
            return new(new Exception("Get None even after time out!"));

        var recvData = data.Value;
        if (recvData.Address != address || (port > 0 && recvData.Port != port))
            return new(new Exception("Receive Data From Wrong Board!"));

        var retPack = WebProtocol.RecvRespPackage.FromBytes(recvData.Data);
        if (!retPack.IsSuccessful)
            return new(new Exception("Not RecvDataPackage!", retPack.Error));

        return retPack.Value;
    }

    /// <summary>
    /// 异步等待数据
    /// </summary>
    /// <param name="address">IP地址</param>
    /// <param name="taskID">[TODO:parameter]</param>
    /// <param name="port">UDP 端口</param>
    /// <param name="timeout">超时时间范围</param>
    /// <returns>接收数据包</returns>
    public async ValueTask<Result<RecvDataPackage>> WaitForDataAsync
        (string address, int taskID, int port = -1, int timeout = 1000)
    {
        var data = await FindDataAsync(address, taskID, timeout);
        if (!data.HasValue)
            return new(new Exception("Get None even after time out!"));

        var recvData = data.Value;
        if (recvData.Address != address || (port >= 0 && recvData.Port != port))
            return new(new Exception("Receive Data From Wrong Board!"));

        var retPack = WebProtocol.RecvDataPackage.FromBytes(recvData.Data);
        if (!retPack.IsSuccessful)
            return new(new Exception("Not RecvDataPackage!", retPack.Error));

        return retPack.Value;
    }

    private async Task ReceiveHandler(byte[] data, IPEndPoint endPoint, DateTime time)
    {
        // 异步锁保护 udpData
        await Task.Run(async () =>
        {
            try
            {
                // Handle RemoteEP
                if (endPoint is null)
                {
                    logger.Debug($"Receive Data from Unknown at {DateTime.Now.ToString()}:");
                    logger.Debug($"  Original Data : {BitConverter.ToString(data).Replace("-", " ")}");
                    return;
                }

                var udpDataObj = await RecordUDPData(data, endPoint, time, Convert.ToInt32(data[1 + 4]));
                // PrintData(udpDataObj);
            }
            catch (Exception e)
            {
                logger.Error($"Got Error when handle receive:{e}");
            }
        });
    }

    private async Task<UDPData> RecordUDPData(byte[] bytes, IPEndPoint remoteEP, DateTime time, int taskID)
    {
        var remoteAddress = remoteEP.Address.ToString();
        var remotePort = remoteEP.Port;
        var data = new UDPData()
        {
            DateTime = time,
            Timestamp = Number.BytesToUInt32(bytes[..4]).Value,
            Address = remoteAddress,
            Port = remotePort,
            TaskID = taskID,
            Data = bytes,
            HasRead = false,
        };

        var key = $"{remoteAddress}-{taskID}";

        try
        {
            using (await udpDataLock.AcquireWriteLockAsync(TimeSpan.FromMilliseconds(5000)))
            {
                var sortedList = udpData.GetOrAdd(key, _ => new SortedList<UInt32, UDPData>());

                // 处理相同时间戳的情况，添加微小的时间差
                var uniqueTime = data.Timestamp;
                while (sortedList.ContainsKey(uniqueTime))
                {
                    logger.Warn(
                        $"Duplicate timestamp detected for {remoteAddress}:{remotePort} at {uniqueTime}.");
                    uniqueTime += 1;
                }

                sortedList.Add(uniqueTime, data);
                // 输出单个数据
                // PrintData(data);
            }
        }
        catch (TimeoutException)
        {
            logger.Error($"Failed to acquire write lock for recording UDP data from {remoteAddress}:{remotePort}");
            throw;
        }

        return data;
    }

    /// <summary>
    /// 输出UDP Data到log中
    /// </summary>
    /// <param name="data">UDP数据</param>
    public string PrintData(UDPData data)
    {
        var bytes = data.Data;
        var sign = bytes[4];
        string recvData = "";
        if (sign == (byte)WebProtocol.PackSign.RecvData)
        {
            var resData = WebProtocol.RecvDataPackage.FromBytes(bytes);
            if (resData.IsSuccessful)
                recvData = resData.Value.Options.ToString();
            else
                recvData = resData.Error.ToString();
        }
        else if (sign == (byte)WebProtocol.PackSign.RecvResp)
        {
            var resData = WebProtocol.RecvRespPackage.FromBytes(bytes);
            if (resData.IsSuccessful)
                recvData = resData.Value.Options.ToString();
            else
                recvData = resData.Error.ToString();
        }
        else
        {
            recvData = Encoding.ASCII.GetString(bytes, 0, bytes.Length);
        }

        logger.Debug($"Receive Data from {data.Address}:{data.Port} at {data.DateTime.ToString()} - {data.Timestamp}:");
        logger.Debug($"  Original Data : {BitConverter.ToString(bytes).Replace("-", " ")}");
        if (recvData.Length != 0) logger.Debug($"  Decoded Data  : {recvData}");
        return $@"
        Receive Data from {data.Address}:{data.Port} at {data.DateTime.ToString()}:
            Original Data : {BitConverter.ToString(bytes).Replace("-", " ")}
            Decoded Data  : {recvData} 
        ";
    }

    /// <summary>
    /// 输出UDP Data数组到log中
    /// </summary>
    /// <param name="dataArray">UDP数据列表</param>
    public void PrintDataArray(IEnumerable<UDPData> dataArray)
    {
        foreach (var data in dataArray)
        {
            logger.Debug(PrintData(data));
        }
    }


    /// <summary>
    /// 将所有数据输出到log中
    /// </summary>
    /// <returns> void </returns>
    public async Task PrintAllDataAsync()
    {
        logger.Debug("Ready Data:");

        try
        {
            using (await udpDataLock.AcquireReadLockAsync(TimeSpan.FromMilliseconds(5000)))
            {
                foreach (var kvp in udpData)
                {
                    foreach (var data in kvp.Value.Values)
                    {
                        logger.Debug(PrintData(data));
                    }
                }
            }
        }
        catch (TimeoutException)
        {
            logger.Error("Failed to acquire read lock for printing all data");
        }
    }

    /// <summary>
    /// 清空指定IP地址的数据
    /// </summary>
    /// <param name="ipAddr">IP地址</param>
    /// <param name="taskID">[TODO:parameter]</param>
    /// <returns>无</returns>
    public void ClearUDPData(string ipAddr, int taskID)
    {
        var key = $"{ipAddr}-{taskID}";

        using (udpDataLock.AcquireWriteLock())
        {
            if (udpData.TryGetValue(key, out var sortedList))
            {
                sortedList.Clear();
            }
        }

    }

    /// <summary>
    /// Start UDP Server
    /// </summary>
    /// <returns>None</returns>
    public void Start()
    {
        if (cancellationTokenSource != null && !cancellationTokenSource.Token.IsCancellationRequested)
        {
            logger.Warn("UDP Server is already running");
            return;
        }

        cancellationTokenSource = new CancellationTokenSource();
        var cancellationToken = cancellationTokenSource.Token;

        try
        {
            foreach (var client in listeners)
            {
                tasks.Add(Task.Run(async () =>
                {
                    while (!cancellationToken.IsCancellationRequested)
                    {
                        try
                        {
                            // 使用 CancellationToken 来取消接收操作
                            var result = await client.ReceiveAsync(cancellationToken);
                            _ = ReceiveHandler(result.Buffer, result.RemoteEndPoint, DateTime.Now);
                        }
                        catch (OperationCanceledException)
                        {
                            logger.Debug("UDP receive operation was cancelled");
                            break;
                        }
                        catch (ObjectDisposedException)
                        {
                            logger.Debug("UDP client was disposed");
                            break;
                        }
                        catch (Exception ex)
                        {
                            if (!cancellationToken.IsCancellationRequested)
                            {
                                logger.Error($"Error in UDP receive: {ex.Message}");
                            }
                        }
                    }
                }, cancellationToken));
            }

            logger.Info("UDP Server started successfully");
        }
        catch (Exception e)
        {
            logger.Error($"Failed to start UDP server: {e}");
            cancellationTokenSource?.Cancel();
            throw;
        }
    }

    /// <summary>
    /// Close UDP Server
    /// </summary>
    /// <returns>None</returns>
    public void Stop()
    {
        if (cancellationTokenSource == null || cancellationTokenSource.Token.IsCancellationRequested)
        {
            logger.Warn("UDP Server is not running or already stopped");
            return;
        }

        try
        {
            logger.Info("Stopping UDP Server...");

            // 取消所有操作
            cancellationTokenSource.Cancel();

            // 等待所有任务完成，设置超时时间
            var waitTasks = Task.WhenAll(tasks);
            if (!waitTasks.Wait(TimeSpan.FromSeconds(5)))
            {
                logger.Warn("Some tasks did not complete within timeout period");
            }

            // 关闭所有UDP客户端
            foreach (var client in listeners)
            {
                try
                {
                    client.Close();
                }
                catch (Exception ex)
                {
                    logger.Warn($"Error closing UDP client: {ex.Message}");
                }
            }

            // 清理任务列表
            tasks.Clear();

            logger.Info("UDP Server stopped successfully");
        }
        catch (Exception ex)
        {
            logger.Error($"Error stopping UDP server: {ex.Message}");
        }
        finally
        {
            cancellationTokenSource?.Dispose();
            cancellationTokenSource = null;
        }
    }

    /// <summary>
    /// 实现IDisposable接口，确保资源正确释放
    /// </summary>
    public void Dispose()
    {
        if (!disposed)
        {
            Stop();

            foreach (var client in listeners)
            {
                client?.Dispose();
            }

            udpDataLock?.Dispose();
            disposed = true;
        }
    }
}
