#nullable enable
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using UnityEngine;
using IGP.Multiplayer.Core;
using IGP.Multiplayer.Models;
using IGP.Multiplayer.Protocol;
using Newtonsoft.Json.Linq;

namespace IGP.Multiplayer.Network
{
    public class IGPNetwork : IIGPNetwork
    {
        private const string LogScope = "p2p";
        private readonly IGPMultiplayerRuntime _manager;
        private readonly object _lock = new object();

        // Sessions: Key = Remote Player ID
        private enum SessionStatus { None, Connected }
        private readonly Dictionary<string, SessionStatus> _sessions = new Dictionary<string, SessionStatus>();
        
        // Remote Player RTT storage (Value = Server RTT in seconds)
        private readonly Dictionary<string, float> _remotePlayerServerRtt = new Dictionary<string, float>();
        private float _lastRttBroadcastTime = 0f;
        private const float RttBroadcastInterval = 2.0f; // Seconds
        private const uint RttMessageType = 1000; // Special type for RTT exchange
        private const string ArenaControlTypeReliableMessageAborted = "reliable-message-aborted";
        private IGPReliableTransportOptions _transportOptions = IGPReliableTransportOptions.Default;
        private IGPReliableMessageReassembler _reassembler;
        
        // Incoming Data Queue
        private struct IncomingPacket
        {
            public IGPPlayerID LocalPeer;
            public IGPGamePeer RemotePeer;
            public byte[] Data;
            public uint MessageType;
            public long Timestamp;
        }
        
        private readonly ConcurrentQueue<IncomingPacket> _packetQueue = new ConcurrentQueue<IncomingPacket>();
        
        // Blocked Message Types
        private readonly HashSet<uint> _blockedTypes = new HashSet<uint>();

        public event Action<IGPDataReceived>? DataReceived;
        internal event Action<IGPDataReceived>? DataReceivedInternal;
        internal event Action<IGPPeerActivity>? PeerActivity;
        public IGPMultiplayerLogLevel LogLevel { get; set; }
        public bool RetainIncomingPacketsForPolling { get; set; } = true;

        internal IGPNetwork(IGPMultiplayerRuntime manager)
        {
            _manager = manager;
            _reassembler = new IGPReliableMessageReassembler(_transportOptions);
        }

        internal void ApplyTransportOptions(IGPReliableTransportOptions options)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            lock (_lock)
            {
                _transportOptions = options;
                _reassembler.Reset();
                _reassembler = new IGPReliableMessageReassembler(_transportOptions);
            }
        }

        public void Update()
        {
            // Periodically broadcast local RTT to other players
            if (_manager.IsP2PDataPlaneReady &&
                Time.time - _lastRttBroadcastTime > RttBroadcastInterval)
            {
                var stats = _manager.AppRTTStats;
                if (stats != null && stats.SampleCount > 0)
                {
                    float avgRtt = stats.AvgRTT;
                    byte[] rttData = BitConverter.GetBytes(avgRtt);
                    // Broadcast to everyone in room (targetId = "")
                    SendData(new IGPPlayerID(_manager.PlayerId), new IGPPlayerID(""), rttData, (uint)rttData.Length, RttMessageType);
                }
                _lastRttBroadcastTime = Time.time;
            }

            CleanupExpiredReliableMessages();
        }

        public void Dispose()
        {
            _reassembler.Reset();
        }

        #region Internal Message Handling

        /// <summary>
        /// Handles incoming P2P data messages pushed from IGPMultiplayerRuntime.
        /// </summary>
        internal void HandleIncomingP2P(
            object? content,
            IGPRealtimeTransport transport = IGPRealtimeTransport.Kcp)
        {
            if (content == null) return;

            try
            {
                var p2pMsg = ConvertToP2PMessagePayload(content);

                if (p2pMsg == null) return;

                CleanupExpiredReliableMessages();

                bool isReliableChunk = IsReliableChunk(p2pMsg);
                string transportChannel = ResolveTransportChannel(p2pMsg);
                uint effectiveMessageType = p2pMsg.reliableMessageType ?? p2pMsg.messageType;

                if (TryHandleArenaControlMessage(p2pMsg, transportChannel))
                {
                    return;
                }

                // Handle RTT Exchange messages (Special internal type)
                if (!isReliableChunk && p2pMsg.messageType == RttMessageType && p2pMsg.data != null)
                {
                    try
                    {
                        var dataBytes = Convert.FromBase64String(p2pMsg.data);
                        if (dataBytes.Length >= 4)
                        {
                            float remoteServerRtt = BitConverter.ToSingle(dataBytes, 0);
                            lock (_lock)
                            {
                                _remotePlayerServerRtt[p2pMsg.senderId] = remoteServerRtt;
                            }
                        }
                    }
                    catch { /* ignore malformed rtt */ }
                    return;
                }

                // Check Blocked
                if (_blockedTypes.Contains(effectiveMessageType)) return;

                var remoteId = p2pMsg.senderId;
                var localId = _manager.PlayerId;

                lock (_lock)
                {
                    if (!_sessions.ContainsKey(remoteId))
                    {
                        _sessions[remoteId] = SessionStatus.Connected;
                        LogInfo("session", $"event=auto-created remote={FormatLogValue(remoteId)} transport={transport}");
                    }
                    
                    if (_sessions[remoteId] == SessionStatus.Connected)
                    {
                        byte[]? dataBytes = TryDecodeIncomingPayload(
                            remoteId,
                            p2pMsg,
                            out var resolvedMessageType,
                            out var acceptedPayload);
                        if (acceptedPayload)
                        {
                            PeerActivity?.Invoke(new IGPPeerActivity
                            {
                                remote_peer = new IGPPlayerID(remoteId),
                                message_type = resolvedMessageType,
                                transport_channel = transportChannel,
                                is_reliable_chunk = isReliableChunk,
                                reliable_chunk_index = p2pMsg.reliableChunkIndex ?? -1,
                                reliable_chunk_count = p2pMsg.reliableChunkCount ?? -1
                            });
                        }

                        if (dataBytes != null && dataBytes.Length > 0)
                        {
                            var packet = new IncomingPacket
                            {
                                LocalPeer = new IGPPlayerID(localId),
                                RemotePeer = new IGPGamePeer(new IGPPlayerID(remoteId), new IGPGameID(_manager.CurrentRoomId)), // Assume same game for p2p_data
                                Data = dataBytes,
                                MessageType = resolvedMessageType,
                                Timestamp = DateTime.Now.Ticks
                            };
                            if (RetainIncomingPacketsForPolling)
                            {
                                _packetQueue.Enqueue(packet);
                            }

                            var received = new IGPDataReceived
                            {
                                remote_peer = new IGPPlayerID(remoteId),
                                data = dataBytes,
                                message_type = resolvedMessageType,
                                Transport = transport,
                            };
                            DataReceivedInternal?.Invoke(received);
                            DataReceived?.Invoke(received);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                LogError(
                    "recv",
                    $"event=parse-failed type=p2p_data error={FormatLogValue(ex.Message)}");
            }
        }

        #endregion

        #region IIGPNetwork Implementation

        public IGPNetworkResult CloseSession(IGPPlayerID local_peer, IGPPlayerID remote_peer)
        {
            lock (_lock)
            {
                if (_sessions.ContainsKey(remote_peer.id))
                {
                    _sessions.Remove(remote_peer.id);
                    LogInfo("session", $"event=closed remote={FormatLogValue(remote_peer.id)}");
                    // Notify remote? 
                    // Usually CloseSession just closes local. Remote detects via events.
                    // For now, we just remove local state.
                    return IGPNetworkResult.kSuccess;
                }
            }
            return IGPNetworkResult.kErrorUnknown;
        }

        public IGPNetworkResult GetSessionState(IGPPlayerID remote_peer, out IGPNetworkSessionState session_state)
        {
            session_state = new IGPNetworkSessionState();
            session_state.remote_peer = remote_peer;
            
            lock (_lock)
            {
                if (_sessions.TryGetValue(remote_peer.id, out var status))
                {
                    session_state.is_connected = (status == SessionStatus.Connected);
                    session_state.is_writable = session_state.is_connected && _manager.IsP2PDataPlaneWritable;
                    return IGPNetworkResult.kSuccess;
                }
            }
            return IGPNetworkResult.kErrorUnknown;
        }

        public IGPNetworkResult SendData(IGPPlayerID local_peer, IGPPlayerID remote_peer, byte[] data_buf, uint data_len, uint message_type = 0)
        {
            // If remote_peer.id is empty, it means broadcast to room
            return InternalSendData(
                local_peer,
                remote_peer.id,
                data_buf,
                data_len,
                message_type,
                allowFragmentation: false,
                standardTransportChannel: IGPP2PTransportChannels.Data,
                targetPlayerIds: null);
        }

        public IGPNetworkResult SendUnreliableData(IGPPlayerID local_peer, IGPPlayerID remote_peer, byte[] data_buf, uint data_len, uint message_type = 0)
        {
            if (data_buf == null || data_len == 0) return IGPNetworkResult.kErrorInvalidParam;
            if (data_len > int.MaxValue) return IGPNetworkResult.kErrorInvalidParam;
            int dataLength = (int)data_len;
            if (dataLength > data_buf.Length) return IGPNetworkResult.kErrorInvalidParam;

            int payloadLimit = _manager.UnreliableUdpPayloadMaxBytes;
            if (dataLength > payloadLimit)
            {
                LogWarning(
                    "send-unreliable",
                    $"event=payload-too-large bytes={data_len} limit={payloadLimit}");
                return IGPNetworkResult.kErrorInvalidParam;
            }

            if (!_manager.IsUnreliableUdpConnected)
            {
                LogError(
                    "send-unreliable",
                    $"event=send-failed reason=udp-not-ready sourcePlayerId={FormatLogValue(local_peer.id)} targetPlayerId={FormatLogValue(remote_peer.id)}");
                return IGPNetworkResult.kErrorServiceNotAvailable;
            }

            string targetId = remote_peer.id ?? string.Empty;
            if (!string.IsNullOrEmpty(targetId))
            {
                lock (_lock)
                {
                    if (!_sessions.ContainsKey(targetId))
                    {
                        _sessions[targetId] = SessionStatus.Connected;
                        LogInfo("session", $"event=auto-created target={FormatLogValue(targetId)} transport=udp-unreliable");
                    }
                }
            }

            var payloadBytes = new byte[dataLength];
            Buffer.BlockCopy(data_buf, 0, payloadBytes, 0, dataLength);
            var payload = new P2PMessagePayload
            {
                senderId = local_peer.id,
                targetId = targetId,
                data = Convert.ToBase64String(payloadBytes),
                messageType = message_type,
                transportChannel = IGPP2PTransportChannels.Data,
                reliable = false
            };

            var msg = new Message
            {
                type = "p2p_data",
                roomId = _manager.CurrentRoomId,
                playerId = local_peer.id,
                targetPlayerId = targetId,
                content = payload,
                reliable = false
            };

            bool sent = _manager.TrySendUnreliableP2PMessage(msg);
            if (!sent)
            {
                LogError(
                    "send-unreliable",
                    $"event=send-failed reason=udp-send-failed sourcePlayerId={FormatLogValue(local_peer.id)} " +
                    $"targetPlayerId={FormatLogValue(targetId)} messageType={message_type} bytes={dataLength}");
                return IGPNetworkResult.kErrorNetworkError;
            }

            return IGPNetworkResult.kSuccess;
        }

        public IGPNetworkResult SendReliableData(IGPPlayerID local_peer, IGPPlayerID remote_peer, byte[] data_buf, uint data_len, uint message_type = 0)
        {
            // If remote_peer.id is empty, it means broadcast to room
            return InternalSendData(
                local_peer,
                remote_peer.id,
                data_buf,
                data_len,
                message_type,
                allowFragmentation: true,
                standardTransportChannel: IGPP2PTransportChannels.Data,
                targetPlayerIds: null);
        }

        internal IGPNetworkResult SendReliableDataToPlayers(
            IGPPlayerID localPeer,
            IReadOnlyList<string> targetPlayerIds,
            byte[] data,
            uint dataLength,
            uint messageType)
        {
            if (targetPlayerIds == null || targetPlayerIds.Count < 2 ||
                !_manager.UsesDataPlaneEnvelopeV1)
            {
                return IGPNetworkResult.kErrorInvalidState;
            }
            return InternalSendData(
                localPeer,
                string.Empty,
                data,
                dataLength,
                messageType,
                allowFragmentation: true,
                standardTransportChannel: IGPP2PTransportChannels.Data,
                targetPlayerIds: targetPlayerIds);
        }

        public IGPNetworkResult SendControlData(IGPPlayerID local_peer, IGPPlayerID remote_peer, byte[] data_buf, uint data_len, uint message_type = 0)
        {
            return InternalSendData(
                local_peer,
                remote_peer.id,
                data_buf,
                data_len,
                message_type,
                allowFragmentation: false,
                standardTransportChannel: IGPP2PTransportChannels.Control,
                targetPlayerIds: null);
        }

        private IGPNetworkResult InternalSendData(
            IGPPlayerID local_peer,
            string targetId,
            byte[] data_buf,
            uint data_len,
            uint message_type,
            bool allowFragmentation,
            string standardTransportChannel,
            IReadOnlyList<string>? targetPlayerIds)
        {
            if (data_buf == null || data_len == 0) return IGPNetworkResult.kErrorInvalidParam;
            if (data_len > int.MaxValue) return IGPNetworkResult.kErrorInvalidParam;
            int dataLength = (int)data_len;
            if (dataLength > data_buf.Length) return IGPNetworkResult.kErrorInvalidParam;

            if (allowFragmentation)
            {
                if (dataLength > _transportOptions.ReliableMessageMaxBytes)
                {
                    LogWarning(
                        "send",
                        $"event=payload-too-large mode=reliable bytes={data_len} limit={_transportOptions.ReliableMessageMaxBytes}");
                    return IGPNetworkResult.kErrorInvalidParam;
                }
            }
            else if (dataLength > _transportOptions.ReliablePayloadMaxBytes)
            {
                LogWarning(
                    "send",
                    $"event=payload-too-large mode=direct bytes={data_len} limit={_transportOptions.ReliablePayloadMaxBytes}");
                return IGPNetworkResult.kErrorInvalidParam;
            }

            // Check session for point-to-point (non-broadcast)
            if (targetPlayerIds != null)
            {
                lock (_lock)
                {
                    foreach (string playerId in targetPlayerIds)
                    {
                        if (string.IsNullOrWhiteSpace(playerId)) return IGPNetworkResult.kErrorInvalidParam;
                        if (!_sessions.ContainsKey(playerId)) _sessions[playerId] = SessionStatus.Connected;
                    }
                }
            }
            else if (!string.IsNullOrEmpty(targetId))
            {
                lock (_lock)
                {
                    // Auto-create session on send if not exists (Initiator)
                    if (!_sessions.ContainsKey(targetId))
                    {
                        _sessions[targetId] = SessionStatus.Connected; // Initiator is implicitly connected
                        LogInfo("session", $"event=auto-created target={FormatLogValue(targetId)}");
                    }
                }
            }

            try
            {
                int payloadCount = 0;
                var messages = new List<Message>();
                string[]? routeTargets = targetPlayerIds == null ? null : new List<string>(targetPlayerIds).ToArray();
                foreach (var payload in CreateOutgoingPayloads(
                    local_peer.id,
                    targetId,
                    data_buf,
                    dataLength,
                    message_type,
                    allowFragmentation,
                    standardTransportChannel))
                {
                    payloadCount += 1;
                    messages.Add(new Message
                    {
                        type = "p2p_data",
                        roomId = _manager.CurrentRoomId,
                        playerId = local_peer.id,
                        targetPlayerId = targetId,
                        content = payload,
                        reliable = true,
                        dataPlaneTargetPlayerIds = routeTargets,
                    });
                }

                IGPTransportSendResult sendResult = _manager.TrySendP2PMessages(
                    messages,
                    isControl: string.Equals(standardTransportChannel, IGPP2PTransportChannels.Control, StringComparison.Ordinal));
                if (sendResult != IGPTransportSendResult.Accepted)
                {
                    var logMessage =
                        $"event=send-rejected result={sendResult} sourcePlayerId={FormatLogValue(local_peer.id)} " +
                        $"targetPlayerId={FormatLogValue(targetId)} messageType={message_type} bytes={dataLength} payloads={payloadCount}";
                    if (sendResult == IGPTransportSendResult.Unavailable ||
                        sendResult == IGPTransportSendResult.Faulted)
                    {
                        LogError("send", logMessage);
                    }
                    else
                    {
                        LogWarning("send", logMessage);
                    }
                    return MapTransportSendResult(sendResult);
                }
                return IGPNetworkResult.kSuccess;
            }
            catch (Exception ex)
            {
                LogWarning(
                    "send",
                    $"event=send-failed localPlayerId={FormatLogValue(_manager.PlayerId)} " +
                    $"sourcePlayerId={FormatLogValue(local_peer.id)} targetPlayerId={FormatLogValue(targetId)} " +
                    $"messageType={message_type} bytes={dataLength} error={FormatLogValue(ex.Message)}");
                return IGPNetworkResult.kErrorNetworkError;
            }
        }

        private static IGPNetworkResult MapTransportSendResult(IGPTransportSendResult result)
        {
            switch (result)
            {
                case IGPTransportSendResult.Accepted:
                    return IGPNetworkResult.kSuccess;
                case IGPTransportSendResult.WouldBlock:
                    return IGPNetworkResult.kErrorWouldBlock;
                case IGPTransportSendResult.InvalidPayload:
                    return IGPNetworkResult.kErrorInvalidParam;
                case IGPTransportSendResult.Faulted:
                    return IGPNetworkResult.kErrorNetworkConnectionClosed;
                case IGPTransportSendResult.Unavailable:
                    return IGPNetworkResult.kErrorServiceNotAvailable;
                default:
                    return IGPNetworkResult.kErrorNetworkError;
            }
        }

        public bool IsDataReady(out IGPPlayerID local_peer, out uint data_len, out uint message_type)
        {
            if (_packetQueue.TryPeek(out var packet))
            {
                local_peer = packet.LocalPeer;
                data_len = (uint)packet.Data.Length;
                message_type = packet.MessageType;
                return true;
            }

            local_peer = default;
            data_len = 0;
            message_type = 0;
            return false;
        }

        public IGPNetworkResult ReadData(IGPPlayerID local_peer, out IGPPlayerID remote_peer, byte[] data_buf, uint data_len, uint message_type = 0)
        {
            remote_peer = default;

            if (_packetQueue.TryDequeue(out var packet))
            {
                remote_peer = packet.RemotePeer.player_id;

                if (data_buf.Length < packet.Data.Length)
                {
                    // Buffer too small, data loss or partial read?
                    // We copy what fits.
                    Array.Copy(packet.Data, data_buf, data_buf.Length);
                    return IGPNetworkResult.kErrorInvalidParam; // Or partial success?
                }

                Array.Copy(packet.Data, data_buf, packet.Data.Length);
                return IGPNetworkResult.kSuccess;
            }

            return IGPNetworkResult.kErrorUnknown;
        }

        public IGPNetworkResult ReadRawData(IGPPlayerID local_peer, out IGPGamePeer remote_game_peer, byte[] data_buf, uint data_len, uint message_type = 0)
        {
            if (_packetQueue.TryDequeue(out var packet))
            {
                remote_game_peer = packet.RemotePeer;

                if (data_buf.Length < packet.Data.Length)
                {
                    Array.Copy(packet.Data, data_buf, data_buf.Length);
                    return IGPNetworkResult.kErrorInvalidParam;
                }

                Array.Copy(packet.Data, data_buf, packet.Data.Length);
                return IGPNetworkResult.kSuccess;
            }

            remote_game_peer = default;
            return IGPNetworkResult.kErrorUnknown;
        }

        public IGPNetworkResult ResolveHostname(string domain, out List<string> ip_list)
        {
            ip_list = new List<string>();
            try
            {
                var ips = Dns.GetHostAddresses(domain);
                foreach (var ip in ips)
                {
                    if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                    {
                        ip_list.Add(ip.ToString());
                    }
                }
                return IGPNetworkResult.kSuccess;
            }
            catch
            {
                return IGPNetworkResult.kErrorUnknown;
            }
        }

        public IGPNetworkResult BlockMessageType(IGPPlayerID local_peer, uint message_type)
        {
            lock (_lock)
            {
                _blockedTypes.Add(message_type);
            }
            return IGPNetworkResult.kSuccess;
        }

        public IGPNetworkResult UnblockMessageType(IGPPlayerID local_peer, uint message_type)
        {
            lock (_lock)
            {
                _blockedTypes.Remove(message_type);
            }
            return IGPNetworkResult.kSuccess;
        }

        public uint GetPeerLatency(IGPPlayerID remote_peer)
        {
            float myRtt = _manager.AppRTTStats?.AvgRTT ?? 0f;
            float hisRtt = 0f;
            
            lock (_lock)
            {
                _remotePlayerServerRtt.TryGetValue(remote_peer.id, out hisRtt);
            }

            // Estimate E2E latency as sum of both RTTs
            // If hisRtt is 0, we fallback to just my RTT (better than nothing)
            return (uint)((myRtt + hisRtt) * 1000);
        }

        /// <summary>
        /// Gets list of all player IDs we have active sessions or RTT data for.
        /// </summary>
        public List<string> GetConnectedPeerIds()
        {
            var peers = new HashSet<string>();
            lock (_lock)
            {
                foreach (var id in _sessions.Keys) peers.Add(id);
                foreach (var id in _remotePlayerServerRtt.Keys) peers.Add(id);
            }
            return new List<string>(peers);
        }

        /// <summary>
        /// Resets all session states and clears packet queues.
        /// </summary>
        public void ResetSessionState()
        {
            lock (_lock)
            {
                _sessions.Clear();
                _remotePlayerServerRtt.Clear();
                _reassembler.Reset();
                while (_packetQueue.TryDequeue(out _)) { }
            }
            LogInfo("session", "event=reset");
        }

        #endregion

        private IEnumerable<P2PMessagePayload> CreateOutgoingPayloads(
            string senderId,
            string targetId,
            byte[] dataBuffer,
            int dataLength,
            uint messageType,
            bool allowFragmentation,
            string standardTransportChannel)
        {
            var payloadBytes = new byte[dataLength];
            Buffer.BlockCopy(dataBuffer, 0, payloadBytes, 0, dataLength);

            if (!allowFragmentation || dataLength <= _transportOptions.ReliablePayloadMaxBytes)
            {
                yield return CreateStandardPayload(senderId, targetId, payloadBytes, messageType, standardTransportChannel);
                yield break;
            }

            string messageId = Guid.NewGuid().ToString("N");
            var chunks = IGPReliableMessageFragmenter.Fragment(
                payloadBytes,
                messageType,
                targetId,
                _transportOptions,
                messageId);

            foreach (var chunk in chunks)
            {
                yield return new P2PMessagePayload
                {
                    senderId = senderId,
                    targetId = targetId,
                    data = Convert.ToBase64String(chunk.Payload),
                    messageType = messageType,
                    transportChannel = IGPP2PTransportChannels.Data,
                    reliable = true,
                    reliableMessageId = chunk.MessageId,
                    reliableChunkIndex = chunk.ChunkIndex,
                    reliableChunkCount = chunk.ChunkCount,
                    reliableTotalBytes = chunk.TotalBytes,
                    reliableMessageType = chunk.MessageType
                };
            }
        }

        private static P2PMessagePayload CreateStandardPayload(
            string senderId,
            string targetId,
            byte[] payloadBytes,
            uint messageType,
            string transportChannel)
        {
            return new P2PMessagePayload
            {
                senderId = senderId,
                targetId = targetId,
                data = Convert.ToBase64String(payloadBytes),
                messageType = messageType,
                transportChannel = transportChannel,
                reliable = true
            };
        }

        private static P2PMessagePayload? ConvertToP2PMessagePayload(object content)
        {
            if (content is JObject jobj)
            {
                return jobj.ToObject<P2PMessagePayload>();
            }

            return Newtonsoft.Json.JsonConvert.DeserializeObject<P2PMessagePayload>(
                Newtonsoft.Json.JsonConvert.SerializeObject(content));
        }

        private static bool IsReliableChunk(P2PMessagePayload payload)
        {
            return !string.IsNullOrWhiteSpace(payload.reliableMessageId) &&
                   payload.reliableChunkIndex.HasValue &&
                   payload.reliableChunkCount.HasValue &&
                   payload.reliableTotalBytes.HasValue &&
                   payload.reliableMessageType.HasValue;
        }

        private static string ResolveTransportChannel(P2PMessagePayload payload)
        {
            return IGPP2PTransportChannels.Resolve(payload.transportChannel, IsReliableChunk(payload));
        }

        private bool TryHandleArenaControlMessage(P2PMessagePayload payload, string transportChannel)
        {
            if (!string.Equals(transportChannel, IGPP2PTransportChannels.Control, StringComparison.Ordinal) ||
                string.IsNullOrWhiteSpace(payload.controlType))
            {
                return false;
            }

            switch (payload.controlType)
            {
                case ArenaControlTypeReliableMessageAborted:
                    string abortConnectionId = !string.IsNullOrWhiteSpace(payload.sourceSenderId)
                        ? payload.sourceSenderId
                        : payload.senderId;
                    bool reassemblyCleared = false;
                    if (!string.IsNullOrWhiteSpace(abortConnectionId) &&
                        !string.IsNullOrWhiteSpace(payload.reliableMessageId))
                    {
                        lock (_lock)
                        {
                            reassemblyCleared = _reassembler.Abort(abortConnectionId, payload.reliableMessageId!);
                        }
                    }

                    LogWarning(
                        "control",
                        "event=reliable-message-aborted " +
                        $"reason={FormatLogValue(payload.reason)} sourcePlayerId={FormatLogValue(abortConnectionId)} " +
                        $"failedTargetPlayerId={FormatLogValue(payload.failedTargetId)} messageId={FormatLogValue(payload.reliableMessageId)} " +
                        $"sequence={payload.transportSequence?.ToString() ?? "-"} reassemblyCleared={reassemblyCleared}");
                    return true;
                default:
                    LogWarning(
                        "control",
                        $"event=ignored type={FormatLogValue(payload.controlType)} reason={FormatLogValue(payload.reason)} " +
                        $"sourcePlayerId={FormatLogValue(payload.sourceSenderId)} failedTargetPlayerId={FormatLogValue(payload.failedTargetId)}");
                    return true;
            }
        }

        private byte[]? TryDecodeIncomingPayload(
            string remoteId,
            P2PMessagePayload payload,
            out uint resolvedMessageType,
            out bool acceptedPayload)
        {
            resolvedMessageType = payload.reliableMessageType ?? payload.messageType;
            acceptedPayload = false;
            if (string.IsNullOrWhiteSpace(payload.data))
            {
                return null;
            }

            if (!IsReliableChunk(payload))
            {
                resolvedMessageType = payload.messageType;
                var decoded = Convert.FromBase64String(payload.data);
                acceptedPayload = decoded.Length > 0;
                return decoded;
            }

            try
            {
                var chunk = new IGPReliableChunk(
                    payload.reliableMessageId!,
                    payload.reliableChunkIndex!.Value,
                    payload.reliableChunkCount!.Value,
                    payload.reliableTotalBytes!.Value,
                    payload.reliableMessageType!.Value,
                    payload.targetId ?? string.Empty,
                    Convert.FromBase64String(payload.data));

                var completed = _reassembler.AddChunk(remoteId, chunk, DateTimeOffset.UtcNow);
                acceptedPayload = true;
                if (completed == null)
                {
                    return null;
                }

                resolvedMessageType = completed.MessageType;
                return completed.Payload;
            }
            catch (Exception ex)
            {
                LogWarning("reassembly", $"event=failed error={FormatLogValue(ex.Message)}");
                return null;
            }
        }

        private void CleanupExpiredReliableMessages()
        {
            var expired = _reassembler.RemoveExpired(DateTimeOffset.UtcNow);
            foreach (var item in expired)
            {
                LogWarning(
                    "reassembly",
                    $"event=timeout connection={FormatLogValue(item.ConnectionId)} messageId={FormatLogValue(item.MessageId)}");
            }
        }

        private void LogInfo(string path, string message)
        {
            IGPMultiplayerLog.Info(LogScope, path, message);
        }

        private void LogWarning(string path, string message)
        {
            IGPMultiplayerLog.Warning(LogScope, path, message);
        }

        private void LogError(string path, string message)
        {
            IGPMultiplayerLog.Error(LogScope, path, message);
        }

        private static string FormatLogValue(string? value)
        {
            return IGPMultiplayerLog.FormatValue(value);
        }
    }
}
