#nullable enable
using System;
using UnityEngine;
using IGP.Multiplayer.Models;

namespace IGP.Multiplayer.Samples
{
    /// <summary>
    /// Sample focused on realtime messaging, state sync, and RPC usage.
    /// </summary>
    public sealed class IGPRealtimeMessagingSample : MonoBehaviour
    {
        [Header("References")]
        [SerializeField] private IGPMultiplayerRuntime? runtimeManager = null;

        [Header("Message")]
        [SerializeField] private string messageType = "sample_message";
        [SerializeField] private string messageText = "hello realtime";

        [Header("State")]
        [SerializeField] private string globalStateKey = "sample:global";
        [SerializeField] private string playerStateKey = "sample:player";

        [Header("RPC")]
        [SerializeField] private string rpcName = "sample_echo";
        [SerializeField] private string rpcMode = "all";

        private void Awake()
        {
            runtimeManager ??= FindObjectOfType<IGPMultiplayerRuntime>();
        }

        private void OnEnable()
        {
            if (runtimeManager == null)
            {
                Debug.LogWarning("[IGP RealtimeSample] IGPMultiplayerRuntime not found.");
                return;
            }

            runtimeManager.MessageReceived += HandleRawMessage;
            runtimeManager.ErrorOccurred += HandleError;
        }

        private void OnDisable()
        {
            if (runtimeManager != null)
            {
                runtimeManager.MessageReceived -= HandleRawMessage;
                runtimeManager.ErrorOccurred -= HandleError;
            }
        }

        public async void SendMessage()
        {
            if (runtimeManager == null)
            {
                return;
            }

            try
            {
                await runtimeManager.SendMessageAsync(new Message
                {
                    type = messageType,
                    roomId = runtimeManager.CurrentRoomId,
                    playerId = runtimeManager.PlayerId,
                    reliable = true,
                    content = new
                    {
                        text = messageText,
                        sentAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                    }
                });

                Debug.Log($"[IGP RealtimeSample] Sent message type={messageType}");
            }
            catch (Exception ex)
            {
                Debug.LogError($"[IGP RealtimeSample] SendMessage failed: {ex.Message}");
            }
        }

        public async void SetGlobalState()
        {
            if (runtimeManager == null)
            {
                return;
            }

            try
            {
                await runtimeManager.SetGlobalStateAsync(globalStateKey, new
                {
                    value = messageText,
                    updatedAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                });
                Debug.Log($"[IGP RealtimeSample] Set global state key={globalStateKey}");
            }
            catch (Exception ex)
            {
                Debug.LogError($"[IGP RealtimeSample] SetGlobalState failed: {ex.Message}");
            }
        }

        public async void SetPlayerState()
        {
            if (runtimeManager == null)
            {
                return;
            }

            try
            {
                await runtimeManager.SetPlayerStateAsync(playerStateKey, new
                {
                    value = messageText,
                    updatedAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                });
                Debug.Log($"[IGP RealtimeSample] Set player state key={playerStateKey}");
            }
            catch (Exception ex)
            {
                Debug.LogError($"[IGP RealtimeSample] SetPlayerState failed: {ex.Message}");
            }
        }

        public async void GetGlobalState()
        {
            if (runtimeManager == null)
            {
                return;
            }

            try
            {
                await runtimeManager.GetStateAsync("global", globalStateKey);
                Debug.Log($"[IGP RealtimeSample] Requested global state key={globalStateKey}");
            }
            catch (Exception ex)
            {
                Debug.LogError($"[IGP RealtimeSample] GetGlobalState failed: {ex.Message}");
            }
        }

        public async void ResetGlobalState()
        {
            if (runtimeManager == null)
            {
                return;
            }

            try
            {
                await runtimeManager.ResetStateAsync("global");
                Debug.Log("[IGP RealtimeSample] Requested global state reset.");
            }
            catch (Exception ex)
            {
                Debug.LogError($"[IGP RealtimeSample] ResetGlobalState failed: {ex.Message}");
            }
        }

        public async void RegisterRpc()
        {
            if (runtimeManager == null)
            {
                return;
            }

            try
            {
                await runtimeManager.RegisterRPCAsync(rpcName);
                Debug.Log($"[IGP RealtimeSample] Registered RPC `{rpcName}`");
            }
            catch (Exception ex)
            {
                Debug.LogError($"[IGP RealtimeSample] RegisterRpc failed: {ex.Message}");
            }
        }

        public async void UnregisterRpc()
        {
            if (runtimeManager == null)
            {
                return;
            }

            try
            {
                await runtimeManager.UnregisterRPCAsync(rpcName);
                Debug.Log($"[IGP RealtimeSample] Unregistered RPC `{rpcName}`");
            }
            catch (Exception ex)
            {
                Debug.LogError($"[IGP RealtimeSample] UnregisterRpc failed: {ex.Message}");
            }
        }

        public async void CallRpc()
        {
            if (runtimeManager == null)
            {
                return;
            }

            try
            {
                var requestId = await runtimeManager.CallRPCAsync(
                    rpcName,
                    new
                    {
                        text = messageText,
                        sentAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                    },
                    rpcMode);

                Debug.Log($"[IGP RealtimeSample] Called RPC `{rpcName}`, requestId={requestId}");
            }
            catch (Exception ex)
            {
                Debug.LogError($"[IGP RealtimeSample] CallRpc failed: {ex.Message}");
            }
        }

        private void HandleRawMessage(IGPMessageReceivedEvent message)
        {
            var incomingMessageType = message.MessageType;
            var content = message.Content;
            if (incomingMessageType == "state_get" && content is StateGetResponseContent stateGet)
            {
                Debug.Log(
                    $"[IGP RealtimeSample] State get scope={stateGet.scope}, key={stateGet.key}, " +
                    $"playerId={stateGet.playerId}, value={SerializePayload(stateGet.value)}");
                return;
            }

            if (incomingMessageType == "state_reset" && content is StateResetResponseContent stateReset)
            {
                Debug.Log(
                    $"[IGP RealtimeSample] State reset scope={stateReset.scope}, " +
                    $"playerId={stateReset.playerId}, success={stateReset.success}");
                return;
            }

            if (incomingMessageType == "rpc_call" && content is RPCCallContent rpcCall)
            {
                Debug.Log(
                    $"[IGP RealtimeSample] RPC call name={rpcCall.name}, requestId={rpcCall.requestId}, " +
                    $"callerPlayerId={rpcCall.callerPlayerId}, data={SerializePayload(rpcCall.data)}");
                return;
            }

            if (incomingMessageType == "rpc_response" && content is RPCResponseContent rpcResponse)
            {
                Debug.Log(
                    $"[IGP RealtimeSample] RPC response name={rpcResponse.name}, " +
                    $"requestId={rpcResponse.requestId}, error={rpcResponse.error}, " +
                    $"data={SerializePayload(rpcResponse.data)}");
                return;
            }

            var payload = content != null ? Newtonsoft.Json.JsonConvert.SerializeObject(content) : "<null>";
            Debug.Log($"[IGP RealtimeSample] Raw message type={incomingMessageType}, payload={payload}");
        }

        private static string SerializePayload(object? payload) =>
            payload != null ? Newtonsoft.Json.JsonConvert.SerializeObject(payload) : "<null>";

        private void HandleError(IGPMultiplayerErrorEvent error)
        {
            Debug.LogError($"[IGP RealtimeSample] Runtime error: {error}");
        }
    }
}
