using IoT3D.Framework;
using IoT3D.Framework.DataModel;
using IoTCenter3D.UnityRpc;
using IoTCenterHost.UnityRpc.SharedInterface;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;

public class UnityRpcService : RpcBase
{
    private bool m_Connected = false;
    IUnityServerService unityServerService;
    IUnityAccountService accountService;
    UserTaken userTaken;
    bool m_Logining = false;
    public LoginResult loginResult;

    private CilentMessageEvent messageEvent = new();
    public override bool Connected => m_Connected;
#if !UNITY_WEBGL
    WebSocketRpcClient client;
#else
    WebGLRpcClient client;
#endif
    private void Awake()
    {
        RPCModule.SetRpc(this);
    }
    public override async void Login(string ipAdress, string user, string pwd)
    {
        userTaken = new UserTaken();
        string[] ips = ipAdress.Split(':');
        if(ips.Length != 2)
        {
            userTaken.message = "ipַ";
            return;
        }
        UserData.UserName = user;
        if (m_Logining)
        {
            return;
        }
        if (Connected)
        {
            OnLogin(user, pwd);
            return;
        }
        try
        {
            CancellationTokenSource cts = new CancellationTokenSource();
#if !UNITY_WEBGL
            client = new WebSocketRpcClient(new RpcClientConfiguration(ips[0], int.Parse(ips[1])), cts.Token);
#else
            client = new WebGLRpcClient(new RpcClientConfiguration(ips[0], int.Parse(ips[1])), cts.Token);
#endif
            client.RegisterRemoteTarget<IUnityAccountService>();
            client.RegisterRemoteTarget<IUnityClientService>();
            client.RegisterRemoteTarget<IUnityServerService>();
            client
                .Starting((registration, context) => {
                    registration.RegisterLocalTarget(new UnityClientService(context,this));
                })
                .Ready(theClient => {

                    Debug.Log("Client connected");
                    accountService = theClient.GetRemoteTarget<IUnityAccountService>();
                    m_Connected = true;
                    OnLogin(user, pwd);
                });
            client.OnStop += (res) =>
            {
                Debug.LogWarning("Stopping client");
            };
            client.OnDisconnected += this.Client_OnDisconnected;
            await client.Run();
        }
        catch (Exception ex)
        {
            Debug.LogError(ex.Message);
            userTaken.message = ex.Message;
            userTaken.succeeded = false;
        }
        finally
        {
            OnLoginChanged?.Invoke(userTaken);
        }
    }

    private void OnLogin(string userName, string passWord)
    {
        try
        {
            accountService.Login(userName, passWord, (int)GwClientType.Unity3D);
            m_Logining = true;
        }
        catch (Exception ex)
        {
            Debug.LogError(ex.Message);
            Debug.Log(ex.StackTrace);
        }

    }
    public async void OnLoginCallBack(string result)
    {
        try
        {
            LoginResponseData response = JsonConvert.DeserializeObject<LoginResponseData>(result);
            // Debug.Log(result);
            if (response != null)
            {
                if (response.Code == 200)
                {
                    m_Logining = true;
                    unityServerService = client.GetRemoteTarget<IUnityServerService>();
                    userTaken.message = response.Description;
                    userTaken.succeeded = true;

                    OnLoginChanged?.Invoke(userTaken);

                    await InitEquipData();

                    loginResult = JsonConvert.DeserializeObject<LoginResult>(response.Result);

                    GWUserItem userItem = JsonConvert.DeserializeObject<GWUserItem>(loginResult.userItem);
                    EquipDataManager.OnInit(userItem);

                    IoTUtility.InitSSO(loginResult.Token);
                    if (!string.IsNullOrEmpty(loginResult.Token))
                    {
                        StartCoroutine(InitWebSSO(loginResult.Token));
                    }
                    return;
                }
                else
                {
                    userTaken.message = response.Description;
                    userTaken.succeeded = false;
                }
            }
            else
            {
                userTaken.message = response.Description;
                userTaken.succeeded = false;
            }
            OnLoginChanged?.Invoke(userTaken);
        }
        catch (Exception ex)
        {
            Debug.LogError(ex.Message);
            Debug.Log(ex.StackTrace);
        }
        m_Logining = false;
    }

    IEnumerator InitWebSSO(string token)
    {
        string ipAdress = PlayerPrefs.GetString("WebAdress");
        if (string.IsNullOrEmpty(ipAdress))
        {
            Debug.LogWarning("WebַδûǷ");
            yield break;
        }

        InternalSSOModel internalSSO = new ()
        {
            Token = token,
            TimeStamp = (long)DateTime.Now.Subtract(new DateTime(1970, 1, 1)).TotalSeconds
        };

        string ssoUrl = $"{ipAdress}/IoT/api/v3/Internal/SSO?Token={internalSSO.Token}&TimeStamp={internalSSO.TimeStamp}&Nonce={internalSSO.Nonce}&Sign={internalSSO.Sign}";
        UnityWebRequest request = UnityWebRequest.Get(ssoUrl);
        request.certificateHandler = new AcceptAllCertificatesSignedWithASpecificPublicKey();
        yield return request.SendWebRequest();
        if (request.result != UnityWebRequest.Result.Success)
        {
            Debug.LogError($"Error:{request.error} SSOURL:{ssoUrl}");
        }
        else
        {
            string responseJson = request.downloadHandler.text;
            ResponsesData responseData = JsonConvert.DeserializeObject<ResponsesData>(responseJson);
            if (responseData != null && responseData.data != null)
            {
                if (responseData.succeeded && !string.IsNullOrEmpty(responseData.data.accessToken))
                {
                    IoTUtility.SetWebSSO(responseData.data);
                    yield break;
                }
                else
                {
                    Debug.LogError("InitWebSS ʧ");
                }
            }
            Debug.LogWarning($"{responseJson} URL:{ipAdress}");
        }
    }
    private void Client_OnDisconnected(StreamJsonRpc.JsonRpcDisconnectedEventArgs obj)
    {
        Debug.LogWarning(obj.Reason);
    }
    private async Task InitEquipData()
    {
        try
        {
           // Debug.LogWarning("");
            var equipdata = await unityServerService.GetTotalEquipData(true);
            var ycdata = await unityServerService.GetTotalYCData(true);
            var yxdata = await unityServerService.GetTotalYXData(true);
            List<EquipBaseData> equips = JsonConvert.DeserializeObject<List<EquipBaseData>>(equipdata);
            List<YcItemData> ycItemDatas = JsonConvert.DeserializeObject<List<YcItemData>>(ycdata);
            List<YxItemData> yxItemDatas = JsonConvert.DeserializeObject<List<YxItemData>>(yxdata);


            Debug.LogWarning($"ң⣺{ycItemDatas.Count}");
            Debug.LogWarning($"ңţ{yxItemDatas.Count}");

            equips?.ForEach(item => RPCModule.UpdateEquipItem(item));
            ycItemDatas?.ForEach(item => RPCModule.UpdateYCItem(item));
            yxItemDatas?.ForEach(item => RPCModule.UpdateYXItem(item));

        }
        catch (Exception ex)
        {
            Debug.LogError("ʼ쳣" + ex);

        }
    }
    public override async void AddEquipStateSubscribe(int equipNo)
    {
        await unityServerService.AddEquipStateSubscribe(equipNo);
    }

    public override void AddReceiveChanged(Action<string> del)
    {
        messageEvent.AddListener((message) => { del?.Invoke(message); });
    }

    public override Task ConfirmedRealTimeEventItem(RealTimeEventItem realTimeEventItem)
    {
        unityServerService.ConfirmedRealTimeEventItem(JsonConvert.SerializeObject(realTimeEventItem));
        return Task.CompletedTask;
    }

    public override async Task<T> GetAsyncSQLData<T>(string sql)
    {
        string result = await unityServerService.GetDataTableFromSQLAsync(sql);
        if(result!=null)
        {
            return JsonConvert.DeserializeObject<T>(result);
        }
        return default;
    }

    public async Task<string> GetAsyncSQLListData(string sql)
    {
        string result = await unityServerService.GetDataTableFromSQLAsync(sql);
        return result;
    }

    public override async Task<List<myCurveData>> GetDataFromCurveAsync(List<DateTime> dateTimes, int eqNo, int ycNo, string type = "C")
    {
        string result = await unityServerService.GetDataFromCurveAsync(dateTimes, eqNo, ycNo, type);
        if (result == null)
            return null;
        return JsonConvert.DeserializeObject<List<myCurveData>>(result);
    }

    public override Task<EquipState> GetEquipState(int eqNo)
    {
        return Task.FromResult(EquipDataManager.GetEquipState(eqNo));
    }

    public async override Task<List<RealTimeEventItem>> GetRealTimeEventListAsync()
    {
        string result = await unityServerService.RealTimeEventList(true);
        return JsonConvert.DeserializeObject<List<RealTimeEventItem>>(result);
    }

    public override async Task<List<SetParm>> GetSetParmsAsync(int equipNo)
    {
        string result = await unityServerService.GetSetParmsAsync(equipNo);
        List<SetParm> setParms = JsonConvert.DeserializeObject<List<SetParm>>(result);
        return setParms;
    }

    public override async Task<string> GetValueExpressionData(string param)
    {
        return await unityServerService.GetValueData(param);
    }

    public override Task<YcItemData> GetYcpItemAsync(int equipNo, int ycNo)
    {
        return Task.FromResult(RPCModule.GetYcItem(equipNo, ycNo));
    }

    public  Task<List<YcItemData>> GetYcpListItemAsync(int equipNo)
    {
        return Task.FromResult(RPCModule.GetYcChangedItems(equipNo));
    }

    public async override Task<string> GetYcValueData(int equipNo, int ycNo)
    {
        return await unityServerService.GetValueData($"$C({equipNo},{ycNo})");
    }


    public override Task<YxItemData> GetYxpItemAsync(int equipNo, int yxNo)
    {
        return Task.FromResult(RPCModule.GetYxItem(equipNo, equipNo));
    }

    public  Task<List<YxItemData>> GetYxpListItemAsync(int equipNo)
    {
        return Task.FromResult(RPCModule.GetYxChangedItems(equipNo));
    }

    public async override Task<string> GetYxValueData(int equipNo, int ycNo)
    {
        return await unityServerService.GetValueData($"$X({equipNo},{ycNo})");
    }

    public  void OnClientReceive(string message)
    {
        messageEvent?.Invoke(message);
    }


    public override void RemoveEquipStateSubscribe(int equipNo)
    {
        unityServerService.RemovedEquipStateSubscribe(equipNo);
    }

    public override void SetParam(int s_no, int n_no)
    {
       // Debug.Log($"{s_no},{n_no},{UserData.UserName}");
        unityServerService?.SetParam(s_no, n_no, UserData.UserName);
    }

    public override void SetParam(int s_no, int n_no, string s_value)
    {
        //Debug.Log($"{s_no},{n_no},{s_value},{UserData.UserName}");
        unityServerService?.SetParam(s_no, n_no, s_value, UserData.UserName);
    }

    public override void SetWuBaoAsync(int equipNo, int ycyxNo, string type)
    {
        unityServerService.SetWuBaoAsync(equipNo, ycyxNo, type);
    }


    public Task<Dictionary<int, EquipState>> GetEquipStateDic(List<int> equipNos)
    {
        Dictionary<int, EquipState> equipStateDic = new Dictionary<int, EquipState>();
        for (int i = 0; i < equipNos.Count; i++)
        {
            EquipBaseData equipEvent = RPCModule.GetEquipDataItem(equipNos[i]);
            if(equipEvent == null)
                continue;
            if(!equipStateDic.ContainsKey(equipNos[i]))
            {
                equipStateDic.Add(equipNos[i], (EquipState)equipEvent.State);
            }
        }
        return Task.FromResult(equipStateDic);
    }

    public async override Task<List<myCurveData>> GetChangedDataFromCurveAsync(DateTime bgn, DateTime end, int eqNo, int ycNo, string type = "C")
    {
        string result = await unityServerService.GetChangedDataFromCurveAsync(bgn, end,eqNo, ycNo, type);
        if (result == null)
            return null;
        return JsonConvert.DeserializeObject<List<myCurveData>>(result);
    }

    public async override Task<IoTEquipEventData> GetEquipEvent(List<int> equipNos, DateTime startTime, DateTime endTime, int pageSize = 20, int pageIndex = 1, IoTEquipEventType eventType = IoTEquipEventType.Equip_Event )
    {
        try
        {
            if (equipNos.Count < 1)
                return null;
            string equipNoList = string.Empty;
            for (int i = 1; i < equipNos.Count; i++)
            {
                equipNoList += i + "#";
            }
            equipNoList = equipNoList.Remove(equipNoList.Length - 1);
            string data = await unityServerService.GetEventByType(startTime, endTime, 1, equipNoList, 0, (int)eventType, pageSize, pageIndex);
            IoTEquipEventData ioTEquipEvent = JsonConvert.DeserializeObject<IoTEquipEventData>(data);
            return ioTEquipEvent;
        }
        catch (Exception ex)
        {
            Debug.LogError(ex);
            return null;
        }
    }
    public override void LoginOut()
    {
#if UNITY_EDITOR
        client?.Stop();
        client?.Dispose();
        Debug.Log("client dispose");
#else 
        Task.Factory.StartNew(() =>
        {
            client?.Stop();
            client?.Dispose();
            Debug.Log("client dispose");
        });
#endif
    }
}
public class CilentMessageEvent : UnityEvent<string>
{
}
public class RpcClientConfiguration : Configuration
{
    public RpcClientConfiguration(string ipAdress,int port)
    {
        Port = port;
        IpAdress = ipAdress;
    }
}
public enum GwClientType
{
    Desktop,// 
    Web,//
    Mobile,//ֻ
    WebServer,//web
    Unity3D,//ά
    Gateway,//
    Dll,//Dll
    ARVRMR,//ǿʵʾʾ
    Other//
}