using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using BestHTTP.WebSocket;
using Cysharp.Threading.Tasks;
using Google.Protobuf;
using Plugins.JNGame.Network.Entity;
using Plugins.JNGame.Network.Util;
using Plugins.JNGame.System;
using Plugins.JNGame.Util;
using UnityEngine;
using Object = System.Object;

namespace Plugins.JNGame.Network
{
    public abstract class JNSocket : SystemBase
    {
        private WebSocket _socket;

        //消息Id
        int _id = 0;

        private UniTaskCompletionSource _onOpen;
        
        //创建通知
        private EventDispatcher _event = new();

        public override async Task OnInit()
        {
            await this.OnConnect();
        }
        
        private async UniTask OnConnect()
        {

            var url = $"{await this.GetUrl()}";
            this._socket = new WebSocket(new Uri(url));

            this._socket.OnOpen += OnOpen;
            this._socket.OnMessage += OnMessageReceived;
            this._socket.OnError += OnError;
            this._socket.OnClosed += OnClosed;
            this._socket.OnBinary += Onbinary;
            
            NSystem.Log($"[JNSocket]初始化WebSocket成功,URL:{url}");
            this._socket.Open();
            
            //等待连接成功
            await (this._onOpen = new UniTaskCompletionSource()).Task;

        }

        private void OnOpen(WebSocket websocket)
        {
            NSystem.Log($"[JNSocket] OnOpen");
            this._onOpen.TrySetResult();
        }

        private void OnMessageReceived(WebSocket websocket, string message)
        {
            NSystem.Log($"[JNSocket] OnMessageReceived");
        }

        private void OnError(WebSocket websocket, Exception ex)
        {
            NSystem.Log($"[JNSocket] OnError");
        }

        private void OnClosed(WebSocket websocket, ushort code, string message)
        {
            NSystem.Log($"[JNSocket] OnClosed");
        }

        private void Onbinary(WebSocket websocket, byte[] data)
        {
            
            NSystem.Log($"[JNSocket] Onbinary");
            Dispatch(NDataUtil.Parse(data));
            
        }
        
        //通知消息
        private void Dispatch(JNetParam data)
        {
            
            //发送消息
            List<Delegate> funs = _event.EventHandlers[$"{data.HId}"];
            funs.ForEach(fun =>
            {
                if(fun.Method.GetParameters().Length == 1 && typeof(IMessage).IsAssignableFrom(fun.Method.GetParameters()[0].ParameterType))
                {
                    var type = fun.Method.GetParameters()[0].ParameterType;
                    var cType = type.GetProperty("Parser", BindingFlags.Static | BindingFlags.Public);
                    if (cType != null)
                    {
                        var methodInfo = cType.PropertyType.GetMethod("ParseFrom",new[]{ typeof(byte[]) });
                        if (methodInfo != null)
                        {
                            var message = methodInfo.Invoke(cType.GetValue(null), new object[] { data.Bytes });
                            fun.Method.Invoke(fun.Target,new []{ message });
                        }
                    }
                    else
                    {
                        fun.Method.Invoke(fun.Target,new [] { (object)null });
                    }
                }
                else
                {
                    fun.Method.Invoke(fun.Target,new object[]{ });
                }
            });

        }
        
        //外部监听消息
        public void AddListener<T>(int hId,Action<T> listener) where T : IMessage
        {
            _event.AddListener($"{hId}",listener);
        }

        public void AddListener(int hId, global::System.Action listener)
        {
            _event.AddListener($"{hId}",listener);
        }
        
        //删除外部监听
        public void RemoveListener<T>(int hId,Action<T> listener) where T : IMessage
        {
            _event.RemoveListener($"{hId}",listener);
        }

        protected abstract UniTask<string> GetUrl();
        
        
        //向服务器发送消息
        public void Send(int hId,IMessage data = null)
        {
            _socket.Send(NDataUtil.Encrypt(JNetParam.Build(this._id++,hId).SetData(data)));
        }
        
    }
}