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

namespace IGP.Multiplayer.Models
{
    /// <summary>
    /// RoomNode realtime message model.
    /// </summary>
    [Serializable]
    public class Message
    {
        public string type = string.Empty;
        public string roomId = string.Empty;
        public string playerId = string.Empty;
        public string targetPlayerId = string.Empty;
        public object? content;
        public bool reliable = true;
        public string timestamp = string.Empty; // Go time.Time parses to string in JSON

        /// <summary>
        /// 创建状态设置消息
        /// </summary>
        public static Message CreateStateSet(
            string roomId,
            string playerId,
            string scope,
            string key,
            object? value,
            string? targetPlayerId = null)
        {
            return new Message
            {
                type = "state_set",
                roomId = roomId,
                playerId = playerId,
                content = new StateSetContent
                {
                    scope = scope,
                    key = key,
                    value = value,
                    playerId = targetPlayerId ?? string.Empty,
                    reliable = true,
                },
                reliable = true,
            };
        }

        public static Message CreateSceneGateSet(string roomId, string playerId, string value)
        {
            return new Message
            {
                type = "scene_gate_set",
                roomId = roomId,
                playerId = playerId,
                content = new SceneGateSetContent { value = value },
                reliable = true,
            };
        }

        public static Message CreateSceneGateGet(string roomId, string playerId)
        {
            return new Message
            {
                type = "scene_gate_get",
                roomId = roomId,
                playerId = playerId,
                content = null,
                reliable = true,
            };
        }

        /// <summary>
        /// 创建状态获取消息（服务端会广播 type=state_get 的响应）
        /// </summary>
        public static Message CreateStateGet(string roomId, string playerId, string scope, string key, string? targetPlayerId = null)
        {
            return new Message
            {
                type = "state_get",
                roomId = roomId,
                playerId = playerId,
                content = new StateGetContent
                {
                    scope = scope,
                    key = key,
                    playerId = targetPlayerId ?? string.Empty
                },
                reliable = true,
            };
        }

        /// <summary>
        /// 创建状态重置消息（scope=global/player）。keysToExclude 可选。
        /// </summary>
        public static Message CreateStateReset(string roomId, string playerId, string scope, string[]? keysToExclude = null)
        {
            return new Message
            {
                type = "state_reset",
                roomId = roomId,
                playerId = playerId,
                content = new StateResetContent
                {
                    scope = scope,
                    keysToExclude = keysToExclude ?? Array.Empty<string>(),
                },
                reliable = true,
            };
        }

        /// <summary>
        /// 创建 RPC 注册消息
        /// </summary>
        public static Message CreateRPCRegister(string roomId, string playerId, string name)
        {
            return new Message
            {
                type = "rpc_register",
                roomId = roomId,
                playerId = playerId,
                content = new RPCRegisterContent { name = name },
                reliable = true,
            };
        }

        /// <summary>
        /// 创建 RPC 取消注册消息
        /// </summary>
        public static Message CreateRPCUnregister(string roomId, string playerId, string name)
        {
            return new Message
            {
                type = "rpc_unregister",
                roomId = roomId,
                playerId = playerId,
                content = new RPCUnregisterContent { name = name },
                reliable = true,
            };
        }

        /// <summary>
        /// 创建 RPC 调用消息
        /// </summary>
        public static Message CreateRPCCall(string roomId, string playerId, string name, object? data = null, string mode = "all", string? requestId = null)
        {
            return new Message
            {
                type = "rpc_call",
                roomId = roomId,
                playerId = playerId,
                content = new RPCCallContent
                {
                    name = name,
                    data = data,
                    mode = mode,
                    requestId = requestId ?? string.Empty,
                },
                reliable = true,
            };
        }

        /// <summary>
        /// 创建队伍变更消息
        /// </summary>
        public static Message CreateTeamChange(string roomId, string playerId, string teamId)
        {
            return new Message
            {
                type = "team_change",
                roomId = roomId,
                playerId = playerId,
                content = new { playerId = playerId, teamId = teamId },
                reliable = true,
            };
        }

        /// <summary>
        /// 创建队伍初始化消息
        /// </summary>
        public static Message CreateTeamInitialize(string roomId, string playerId, Team[] teams)
        {
            return new Message
            {
                type = "team_initialize",
                roomId = roomId,
                playerId = playerId,
                content = new { teams = teams },
                reliable = true,
            };
        }

        /// <summary>
        /// 创建队伍状态更新消息
        /// </summary>
        public static Message CreateTeamStatus(string roomId, string playerId, object status)
        {
            return new Message
            {
                type = "team_status",
                roomId = roomId,
                playerId = playerId,
                content = status,
                reliable = true,
            };
        }

        /// <summary>
        /// 创建 RPC 响应消息；RoomNode 根据 targetPlayerId 转发给调用者。
        /// </summary>
        public static Message CreateRPCResponse(
            string roomId,
            string playerId,
            string targetPlayerId,
            string requestId,
            string name,
            object? data,
            string? error = null)
        {
            return new Message
            {
                type = "rpc_response",
                roomId = roomId,
                playerId = playerId,
                targetPlayerId = targetPlayerId,
                content = new RPCResponseContent
                {
                    requestId = requestId,
                    name = name,
                    data = data,
                    error = error ?? string.Empty,
                },
                reliable = true,
            };
        }

    }

    [Serializable]
    public class StateSetContent
    {
        public string scope = string.Empty;
        public string key = string.Empty;
        public object? value;
        public string playerId = string.Empty;
        public bool reliable;

        public T GetValue<T>() => IGPRealtimeContentConverter.ConvertPayload<T>(value);
    }

    [Serializable]
    public class SceneGateSetContent
    {
        public string value = string.Empty;
    }

    [Serializable]
    public class SceneGateGetContent
    {
        public string value = string.Empty;
    }

    [Serializable]
    public class StateGetContent
    {
        public string scope = string.Empty;
        public string key = string.Empty;
        public string playerId = string.Empty;
    }

    [Serializable]
    public class StateResetContent
    {
        public string scope = string.Empty;
        public string[] keysToExclude = Array.Empty<string>();
    }

    /// <summary>
    /// Content returned by RoomNode for a state_get message.
    /// </summary>
    [Serializable]
    public class StateGetResponseContent
    {
        public string scope = string.Empty;
        public string key = string.Empty;
        public object? value;
        public string playerId = string.Empty;

        public T GetValue<T>() => IGPRealtimeContentConverter.ConvertPayload<T>(value);
    }

    /// <summary>
    /// Content broadcast by RoomNode after a state_reset succeeds.
    /// </summary>
    [Serializable]
    public class StateResetResponseContent
    {
        public string scope = string.Empty;
        public string[] keysToExclude = Array.Empty<string>();
        public string playerId = string.Empty;
        public bool success;
    }

    [Serializable]
    public class RPCRegisterContent
    {
        public string name = string.Empty;
    }

    [Serializable]
    public class RPCUnregisterContent
    {
        public string name = string.Empty;
    }

    [Serializable]
    public class RPCCallContent
    {
        public string name = string.Empty;
        public object? data;
        public string mode = "all";
        public string requestId = string.Empty;
        [JsonIgnore] public string callerPlayerId = string.Empty;

        public T GetData<T>() => IGPRealtimeContentConverter.ConvertPayload<T>(data);
    }

    /// <summary>
    /// Content forwarded by RoomNode for an rpc_response message.
    /// </summary>
    [Serializable]
    public class RPCResponseContent
    {
        public string requestId = string.Empty;
        public string name = string.Empty;
        public object? data;
        public string error = string.Empty;

        public bool HasError => !string.IsNullOrEmpty(error);
        public T GetData<T>() => IGPRealtimeContentConverter.ConvertPayload<T>(data);
    }

    internal static class IGPRealtimeContentConverter
    {
        internal static object? ConvertIncoming(Message message)
        {
            if (message == null)
            {
                throw new ArgumentNullException(nameof(message));
            }

            if (string.Equals(message.type, "state_set", StringComparison.OrdinalIgnoreCase))
            {
                return ConvertContent<StateSetContent>(message.content);
            }

            if (string.Equals(message.type, "scene_gate_set", StringComparison.OrdinalIgnoreCase))
            {
                return ConvertContent<SceneGateSetContent>(message.content);
            }

            if (string.Equals(message.type, "scene_gate_get", StringComparison.OrdinalIgnoreCase))
            {
                return ConvertContent<SceneGateGetContent>(message.content);
            }

            if (string.Equals(message.type, "state_get", StringComparison.OrdinalIgnoreCase))
            {
                return ConvertContent<StateGetResponseContent>(message.content);
            }

            if (string.Equals(message.type, "state_reset", StringComparison.OrdinalIgnoreCase))
            {
                return ConvertContent<StateResetResponseContent>(message.content);
            }

            if (string.Equals(message.type, "rpc_call", StringComparison.OrdinalIgnoreCase))
            {
                var content = ConvertContent<RPCCallContent>(message.content);
                if (content != null)
                {
                    content.callerPlayerId = message.playerId;
                }
                return content;
            }

            if (string.Equals(message.type, "rpc_response", StringComparison.OrdinalIgnoreCase))
            {
                return ConvertContent<RPCResponseContent>(message.content);
            }

            return message.content;
        }

        internal static T ConvertPayload<T>(object? payload)
        {
            if (payload == null)
            {
                return default!;
            }

            if (payload is T typed)
            {
                return typed;
            }

            if (payload is JToken token)
            {
                return token.ToObject<T>()!;
            }

            return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(payload))!;
        }

        private static T? ConvertContent<T>(object? content) where T : class
        {
            if (content == null)
            {
                return null;
            }

            if (content is T typed)
            {
                return typed;
            }

            if (content is JToken token)
            {
                return token.ToObject<T>();
            }

            return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(content));
        }
    }
}
