#nullable enable
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using IGP.UnitySDK;
using IGP.UnitySDK.Models;

namespace IGP.UnitySDK.Core
{
    /// <summary>
    /// Hosted WebSocket façade. Public SDK message/state/RPC APIs remain available,
    /// but all transport is delegated to the Curio desktop host session bridge.
    /// </summary>
    internal sealed class IGPHostedRealtimeClient : IDisposable
    {
        private IGPHostSessionClient? hostSession;

        public string? RoomId { get; private set; }
        public string? PlayerId { get; private set; }
        public bool IsConnected { get; private set; }

        public event Action<Message>? MessageReceived;
        public event Action<bool>? ConnectionStateChanged;
        public event Action<string>? ErrorOccurred;

        public IGPHostedRealtimeClient(string _unusedWsUrl = "")
        {
        }

        public Task ConnectAsync(string roomId, string playerId, string token, CancellationToken cancellationToken = default)
        {
            throw new IGPSDKException("Direct room connection is not supported; use Curio desktop hosted bootstrap");
        }

        public Task DisconnectAsync()
        {
            ClearHostedSession();
            return Task.CompletedTask;
        }

        internal void AttachHostedSession(IGPHostSessionClient session, string roomId, string playerId)
        {
            if (session == null)
            {
                throw new ArgumentNullException(nameof(session));
            }

            ClearHostedSession();

            hostSession = session;
            RoomId = roomId;
            PlayerId = playerId;

            hostSession.MessageReceived += HandleMessage;
            hostSession.ErrorOccurred += HandleHostSessionError;
            hostSession.Detached += HandleHostSessionDetached;
        }

        internal void UpdateHostedContext(string roomId, string playerId)
        {
            RoomId = roomId;
            PlayerId = playerId;
        }

        internal void UpdateHostedConnectionState(bool connected)
        {
            if (IsConnected == connected)
            {
                return;
            }

            IsConnected = connected;
            ConnectionStateChanged?.Invoke(connected);
        }

        internal void ClearHostedSession()
        {
            if (hostSession != null)
            {
                hostSession.MessageReceived -= HandleMessage;
                hostSession.ErrorOccurred -= HandleHostSessionError;
                hostSession.Detached -= HandleHostSessionDetached;
                hostSession = null;
            }

            RoomId = null;
            PlayerId = null;
            UpdateHostedConnectionState(false);
        }

        public async Task SendMessageAsync(Message message)
        {
            var session = EnsureHostedSession();
            await session.SendMessageAsync(message);
        }

        public async Task SendPingAsync()
        {
            var session = EnsureHostedSession();
            await session.SendPingAsync();
        }

        public async Task SetStateAsync(string scope, string key, object? value, string? targetPlayerId = null)
        {
            var session = EnsureHostedSession();
            await session.SetStateAsync(scope, key, value, targetPlayerId);
        }

        public async Task GetStateAsync(string scope, string key, string? targetPlayerId = null)
        {
            var session = EnsureHostedSession();
            await session.GetStateAsync(scope, key, targetPlayerId);
        }

        public async Task ResetStateAsync(string scope, string[]? keysToExclude = null)
        {
            var session = EnsureHostedSession();
            await session.ResetStateAsync(scope, keysToExclude);
        }

        public async Task RegisterRPCAsync(string name)
        {
            var session = EnsureHostedSession();
            await session.RegisterRPCAsync(name);
        }

        public async Task UnregisterRPCAsync(string name)
        {
            var session = EnsureHostedSession();
            await session.UnregisterRPCAsync(name);
        }

        public async Task<string> CallRPCAsync(string name, object? data = null, string mode = "all", string? requestId = null)
        {
            var session = EnsureHostedSession();
            return await session.CallRPCAsync(name, data, mode, requestId);
        }

        public void Dispose()
        {
            ClearHostedSession();
        }

        private IGPHostSessionClient EnsureHostedSession()
        {
            if (hostSession == null || !hostSession.IsAttached)
            {
                throw new IGPSDKException("IGP hosted session is not attached");
            }

            if (!IsConnected)
            {
                throw new IGPSDKException("IGP hosted realtime connection is not ready");
            }

            return hostSession;
        }

        private void HandleMessage(Message message)
        {
            MessageReceived?.Invoke(message);
        }

        private void HandleHostSessionDetached(string reason)
        {
            UpdateHostedConnectionState(false);
            ErrorOccurred?.Invoke(reason);
        }

        private void HandleHostSessionError(string code, string message)
        {
            ErrorOccurred?.Invoke(message);
        }
    }

    /// <summary>
    /// 房间列表包装类
    /// </summary>
    [Serializable]
    public class IGPRoomList
    {
        public List<Room> rooms = new List<Room>();
    }
}
