#nullable enable
using UnityEngine;
using UnityEngine.Events;
using System;
using IGP.UnitySDK.Models;
using Newtonsoft.Json.Linq;

namespace IGP.UnitySDK
{
    /// <summary>
    /// IGP Unity事件系统 - 提供类型安全的事件定义和处理
    /// </summary>
    
    [Serializable]
    public class IGPConnectionEventDetailed : UnityEvent<bool> { }
    
    [Serializable]
    public class IGPMessageEventDetailed : UnityEvent<IGPMessageData> { }
    
    [Serializable]
    public class IGPErrorEventDetailed : UnityEvent<IGPErrorData> { }
    
    [Serializable]
    public class IGPRoomEventDetailed : UnityEvent<Room> { }

    [Serializable]
    public class IGPMapChangedEventDetailed : UnityEvent<IGPMapChangeData> { }
    
    [Serializable]
    public class IGPPlayerEvent : UnityEvent<IGPPlayerData> { }
    
    [Serializable]
    public class IGPStateChangeEvent : UnityEvent<IGPStateChangeData> { }
    
    [Serializable]
    public class IGPRPCEvent : UnityEvent<IGPRPCData> { }
    
    /// <summary>
    /// 消息数据结构
    /// </summary>
    [Serializable]
    public class IGPMessageData
    {
        public string type = string.Empty;
        public string roomId = string.Empty;
        public string playerId = string.Empty;
        public string contentJson = string.Empty;
        public bool reliable;
        public long timestamp;
        
        public T? GetContent<T>()
        {
            try
            {
                if (string.IsNullOrEmpty(contentJson)) return default;
                return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(contentJson);
            }
            catch (Exception e)
            {
                Debug.LogError($"[IGP] Failed to parse message content: {e.Message}");
                return default;
            }
        }
    }
    
    /// <summary>
    /// 错误数据结构
    /// </summary>
    [Serializable]
    public class IGPErrorData
    {
        public string code = string.Empty;
        public string message = string.Empty;
        public string details = string.Empty;
        public string roomId = string.Empty;
        public string playerId = string.Empty;
        public long timestamp;
    }
    
    /// <summary>
    /// 玩家数据结构
    /// </summary>
    [Serializable]
    public class IGPPlayerData
    {
        public string id = string.Empty;
        public string name = string.Empty;
        public string color = string.Empty;
        public string avatar = string.Empty;
        public int avatarIndex;
        public bool isHost;
        public long joinedAt;
        public object? customData;
    }
    
    /// <summary>
    /// 游戏动作数据结构
    /// </summary>
    [Serializable]
    public class IGPGameActionData
    {
        public string action = string.Empty;
        public object? data;
        public string playerId = string.Empty;
        public string roomId = string.Empty;
        public long timestamp;
        public bool reliable;
    }
    
    /// <summary>
    /// 状态变更数据结构
    /// </summary>
    [Serializable]
    public class IGPStateChangeData
    {
        public string scope = string.Empty;
        public string key = string.Empty;
        public object? value;
        public object? oldValue;
        public string playerId = string.Empty;
        public string roomId = string.Empty;
        public long timestamp;
    }
    
    /// <summary>
    /// RPC数据结构
    /// </summary>
    [Serializable]
    public class IGPRPCData
    {
        public string name = string.Empty;
        public object? data;
        public string mode = string.Empty;
        public string requestId = string.Empty;
        public string playerId = string.Empty;
        public string roomId = string.Empty;
        public long timestamp;
        public object? response;
        public bool isResponse;
    }
    
    /// <summary>
    /// 事件管理器 - 统一管理所有 IGP 相关事件
    /// </summary>
    public class IGPEventManager : MonoBehaviour
    {
        [Header("Connection Events")]
        public IGPConnectionEventDetailed onConnected = new IGPConnectionEventDetailed();
        public IGPConnectionEventDetailed onDisconnected = new IGPConnectionEventDetailed();
        public IGPConnectionEventDetailed onConnectionStateChanged = new IGPConnectionEventDetailed();
        
        [Header("Message Events")]
        public IGPMessageEventDetailed onMessageReceived = new IGPMessageEventDetailed();
        
        [Header("Room Events")]
        public IGPRoomEventDetailed onRoomCreated = new IGPRoomEventDetailed();
        public IGPRoomEventDetailed onRoomJoined = new IGPRoomEventDetailed();
        public IGPRoomEventDetailed onRoomLeft = new IGPRoomEventDetailed();
        public IGPRoomEventDetailed onRoomUpdated = new IGPRoomEventDetailed();
        public IGPMapChangedEventDetailed onMapChanged = new IGPMapChangedEventDetailed();
        public UnityEvent onGameStarted = new UnityEvent();
        public UnityEvent onGameEnded = new UnityEvent();
        
        [Header("Player Events")]
        public IGPPlayerEvent onPlayerJoined = new IGPPlayerEvent();
        public IGPPlayerEvent onPlayerLeft = new IGPPlayerEvent();
        public IGPPlayerEvent onPlayerUpdated = new IGPPlayerEvent();
        public IGPPlayerEvent onHostTransferred = new IGPPlayerEvent();
        
        [Header("State Events")]
        public IGPStateChangeEvent onGlobalStateChanged = new IGPStateChangeEvent();
        public IGPStateChangeEvent onPlayerStateChanged = new IGPStateChangeEvent();
        public IGPStateChangeEvent onStateChanged = new IGPStateChangeEvent();
        
        [Header("RPC Events")]
        public IGPRPCEvent onRPCCall = new IGPRPCEvent();
        public IGPRPCEvent onRPCResponse = new IGPRPCEvent();
        
        [Header("Error Events")]
        public IGPErrorEventDetailed onError = new IGPErrorEventDetailed();
        public IGPErrorEventDetailed onConnectionError = new IGPErrorEventDetailed();
        public IGPErrorEventDetailed onRoomError = new IGPErrorEventDetailed();
        
        [Header("System Events")]
        public UnityEvent onHeartbeatSent = new UnityEvent();
        public UnityEvent onHeartbeatReceived = new UnityEvent();
        public UnityEvent onReconnecting = new UnityEvent();
        public UnityEvent onReconnected = new UnityEvent();
        
        private IGPRuntimeManager? arenaManager;
        
        void Start()
        {
            arenaManager = FindAnyObjectByType<IGPRuntimeManager>();
            if (arenaManager != null)
            {
                // 连接事件
                arenaManager.onConnectionStateChanged.AddListener(OnConnectionStateChanged);
                
                // 消息事件
                arenaManager.onMessageReceived.AddListener(OnMessageReceived);
                
                // 房间事件
                arenaManager.onRoomJoined.AddListener(OnRoomJoined);
                arenaManager.onRoomUpdated.AddListener(OnRoomUpdated);
                arenaManager.onRoomLeft.AddListener(OnRoomLeft);
                arenaManager.onMapChanged.AddListener(OnMapChanged);
                arenaManager.HostedRoomEventReceived += OnHostedRoomEvent;
                
                // 错误事件
                arenaManager.onError.AddListener(OnError);
                
                // 心跳事件
                arenaManager.onHeartbeatSent.AddListener(OnHeartbeatSent);
            }
            else
            {
                Debug.LogError("[IGPEventManager] IGPRuntimeManager not found!");
            }
        }
        
        void OnDestroy()
        {
            if (arenaManager != null)
            {
                arenaManager.onConnectionStateChanged.RemoveListener(OnConnectionStateChanged);
                arenaManager.onMessageReceived.RemoveListener(OnMessageReceived);
                arenaManager.onRoomJoined.RemoveListener(OnRoomJoined);
                arenaManager.onRoomUpdated.RemoveListener(OnRoomUpdated);
                arenaManager.onRoomLeft.RemoveListener(OnRoomLeft);
                arenaManager.onMapChanged.RemoveListener(OnMapChanged);
                arenaManager.HostedRoomEventReceived -= OnHostedRoomEvent;
                arenaManager.onError.RemoveListener(OnError);
                arenaManager.onHeartbeatSent.RemoveListener(OnHeartbeatSent);
            }
        }
        
        #region Event Handlers
        
        private void OnConnectionStateChanged(bool connected)
        {
            if (connected)
            {
                onConnected?.Invoke(true);
                onConnectionStateChanged?.Invoke(true);
            }
            else
            {
                onDisconnected?.Invoke(false);
                onConnectionStateChanged?.Invoke(false);
            }
        }
        
        private void OnMessageReceived(string messageType, object? content)
        {
            if (this == null) return;
            var messageData = new IGPMessageData
            {
                type = messageType,
                roomId = arenaManager?.CurrentRoomId ?? string.Empty,
                playerId = arenaManager?.PlayerId ?? string.Empty,
                contentJson = content != null ? Newtonsoft.Json.JsonConvert.SerializeObject(content) : string.Empty,
                timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds()
            };
            
            onMessageReceived?.Invoke(messageData);
            DispatchTypedMessageEvents(messageType, content, messageData);
        }
        
        private void OnRoomJoined(Room roomData)
        {
            onRoomJoined?.Invoke(roomData);
        }
        
        private void OnRoomLeft(Room roomData)
        {
            onRoomLeft?.Invoke(roomData);
        }

        private void OnRoomUpdated(Room roomData)
        {
            onRoomUpdated?.Invoke(roomData);
        }

        private void OnMapChanged(IGPMapChangeData mapChange)
        {
            onMapChanged?.Invoke(mapChange);
        }

        private void OnHostedRoomEvent(IGPHostSessionRoomEvent roomEvent)
        {
            if (roomEvent == null)
            {
                return;
            }

            switch (roomEvent.EventType)
            {
                case IGPHostSessionRoomEventType.PlayerJoined:
                    if (roomEvent.Player != null)
                    {
                        onPlayerJoined?.Invoke(ToIGPPlayerData(roomEvent.Player, arenaManager?.CurrentRoomData?.hostId));
                    }
                    break;
                case IGPHostSessionRoomEventType.PlayerLeft:
                    if (roomEvent.Player != null)
                    {
                        onPlayerLeft?.Invoke(ToIGPPlayerData(roomEvent.Player, roomEvent.PreviousHostId));
                    }
                    break;
                case IGPHostSessionRoomEventType.HostTransferred:
                    if (roomEvent.Player != null)
                    {
                        onHostTransferred?.Invoke(ToIGPPlayerData(roomEvent.Player, roomEvent.CurrentHostId));
                    }
                    break;
                case IGPHostSessionRoomEventType.GameStarted:
                    onGameStarted?.Invoke();
                    break;
                case IGPHostSessionRoomEventType.GameEnded:
                    onGameEnded?.Invoke();
                    break;
            }
        }
        
        private void OnError(string errorMessage)
        {
            var errorData = new IGPErrorData
            {
                code = "UNITY_ERROR",
                message = errorMessage,
                timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds()
            };
            
            onError?.Invoke(errorData);
        }
        
        private void OnHeartbeatSent()
        {
            onHeartbeatSent?.Invoke();
        }

        private void DispatchTypedMessageEvents(string messageType, object? content, IGPMessageData messageData)
        {
            switch (messageType)
            {
                case "ping":
                    onHeartbeatReceived?.Invoke();
                    break;
                case "state_set":
                case "state_get":
                case "state_reset":
                    EmitStateChangeEvent(messageType, content, messageData);
                    break;
                case "rpc_call":
                    EmitRpcEvent(content, messageData, false);
                    break;
                case "rpc_response":
                    EmitRpcEvent(content, messageData, true);
                    break;
            }
        }

        private IGPPlayerData ToIGPPlayerData(Player player, string? hostId)
        {
            return new IGPPlayerData
            {
                id = player.id,
                name = player.name,
                color = player.color,
                avatar = player.avatar,
                avatarIndex = player.avatarIndex,
                isHost = string.Equals(player.id, hostId, StringComparison.Ordinal),
                customData = new
                {
                    teamId = player.teamId,
                    state = player.state,
                    isBot = player.isBot,
                },
            };
        }

        private void EmitStateChangeEvent(string messageType, object? content, IGPMessageData messageData)
        {
            var payload = ToJObject(content);
            var scope = payload?.Value<string>("scope") ?? string.Empty;
            var key = payload?.Value<string>("key");
            if (string.IsNullOrEmpty(key) && string.Equals(messageType, "state_reset", StringComparison.Ordinal))
            {
                key = "*";
            }

            var data = new IGPStateChangeData
            {
                scope = scope,
                key = key ?? string.Empty,
                value = payload?["value"]?.ToObject<object>(),
                oldValue = payload?["oldValue"]?.ToObject<object>(),
                playerId = payload?.Value<string>("playerId") ?? string.Empty,
                roomId = arenaManager?.CurrentRoomId ?? string.Empty,
                timestamp = messageData.timestamp,
            };

            onStateChanged?.Invoke(data);
            if (string.Equals(scope, "global", StringComparison.OrdinalIgnoreCase))
            {
                onGlobalStateChanged?.Invoke(data);
            }
            else if (string.Equals(scope, "player", StringComparison.OrdinalIgnoreCase))
            {
                onPlayerStateChanged?.Invoke(data);
            }
        }

        private void EmitRpcEvent(object? content, IGPMessageData messageData, bool isResponse)
        {
            var payload = ToJObject(content);
            var rpcData = new IGPRPCData
            {
                name = payload?.Value<string>("name") ?? string.Empty,
                data = payload?["data"]?.ToObject<object>(),
                mode = payload?.Value<string>("mode") ?? string.Empty,
                requestId = payload?.Value<string>("requestId") ?? string.Empty,
                playerId = messageData.playerId,
                roomId = messageData.roomId,
                timestamp = messageData.timestamp,
                response = isResponse ? payload?["data"]?.ToObject<object>() : null,
                isResponse = isResponse,
            };

            if (isResponse)
            {
                onRPCResponse?.Invoke(rpcData);
            }
            else
            {
                onRPCCall?.Invoke(rpcData);
            }
        }

        private static JObject? ToJObject(object? content)
        {
            if (content == null)
            {
                return null;
            }

            if (content is JObject jObject)
            {
                return jObject;
            }

            try
            {
                return JObject.FromObject(content);
            }
            catch
            {
                return null;
            }
        }
        
        #endregion
        
        #region Public Methods
        
        /// <summary>
        /// 手动触发玩家加入事件
        /// </summary>
        public void TriggerPlayerJoined(IGPPlayerData playerData)
        {
            onPlayerJoined?.Invoke(playerData);
        }
        
        /// <summary>
        /// 手动触发玩家离开事件
        /// </summary>
        public void TriggerPlayerLeft(IGPPlayerData playerData)
        {
            onPlayerLeft?.Invoke(playerData);
        }
        
        /// <summary>
        /// 手动触发游戏开始事件
        /// </summary>
        public void TriggerGameStarted()
        {
            onGameStarted?.Invoke();
        }
        
        /// <summary>
        /// 手动触发游戏结束事件
        /// </summary>
        public void TriggerGameEnded()
        {
            onGameEnded?.Invoke();
        }
        
        /// <summary>
        /// 手动触发状态变更事件
        /// </summary>
        public void TriggerStateChange(string scope, string key, object? value, object? oldValue = null)
        {
            var stateChangeData = new IGPStateChangeData
            {
                scope = scope,
                key = key,
                value = value,
                oldValue = oldValue,
                timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds()
            };
            
            onStateChanged?.Invoke(stateChangeData);
            
            if (scope == "global")
            {
                onGlobalStateChanged?.Invoke(stateChangeData);
            }
            else if (scope == "player")
            {
                onPlayerStateChanged?.Invoke(stateChangeData);
            }
        }
        
        /// <summary>
        /// 手动触发RPC调用事件
        /// </summary>
        public void TriggerRPCCall(string name, object? data, string mode = "all", string? requestId = null)
        {
            var rpcData = new IGPRPCData
            {
                name = name,
                data = data,
                mode = mode,
                requestId = requestId ?? System.Guid.NewGuid().ToString("N").Substring(0, 8),
                timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
                isResponse = false
            };
            
            onRPCCall?.Invoke(rpcData);
        }
        
        #endregion
        
        #region Utility Methods
        
        /// <summary>
        /// 清空所有事件监听器
        /// </summary>
        public void ClearAllListeners()
        {
            onConnected?.RemoveAllListeners();
            onDisconnected?.RemoveAllListeners();
            onConnectionStateChanged?.RemoveAllListeners();
            onMessageReceived?.RemoveAllListeners();
            onRoomCreated?.RemoveAllListeners();
            onRoomJoined?.RemoveAllListeners();
            onRoomLeft?.RemoveAllListeners();
            onRoomUpdated?.RemoveAllListeners();
            onMapChanged?.RemoveAllListeners();
            onGameStarted?.RemoveAllListeners();
            onGameEnded?.RemoveAllListeners();
            onPlayerJoined?.RemoveAllListeners();
            onPlayerLeft?.RemoveAllListeners();
            onPlayerUpdated?.RemoveAllListeners();
            onHostTransferred?.RemoveAllListeners();
            onGlobalStateChanged?.RemoveAllListeners();
            onPlayerStateChanged?.RemoveAllListeners();
            onStateChanged?.RemoveAllListeners();
            onRPCCall?.RemoveAllListeners();
            onRPCResponse?.RemoveAllListeners();
            onError?.RemoveAllListeners();
            onConnectionError?.RemoveAllListeners();
            onRoomError?.RemoveAllListeners();
            onHeartbeatSent?.RemoveAllListeners();
            onHeartbeatReceived?.RemoveAllListeners();
            onReconnecting?.RemoveAllListeners();
            onReconnected?.RemoveAllListeners();
        }
        
        #endregion
    }
}
