using System;
using System.Threading.Tasks;
using WebSocketSharp;
using Socket = WebSocketSharp.WebSocket;
using Logger = InABox.Core.Logger;
using InABox.Core;
using InABox.Clients;
using InABox.WebSocket.Shared;

namespace InABox.Client.WebSocket
{
    public class WebSocketClient : IDisposable
    {
        private Socket Socket;
        private Guid Session;
        private bool Closed = false;

        public WebSocketClient(string url, int port, Guid session)
        {
            Session = session;
            Uri uri = new Uri("http://" + url);
            Socket = new Socket($"ws://{uri.Host}:{port}/push");
            Socket.OnOpen += Socket_OnOpen;
            Socket.OnError += Socket_OnError;
            Socket.OnClose += Socket_OnClose;
            Socket.OnMessage += Socket_OnMessage;
            Socket.Connect();

            // Time to wait before disconnect - the default meant that the client disconnected during debugging, since the ping would fail
            Socket.WaitTime = TimeSpan.FromMinutes(10); 
        }

        private void Socket_OnMessage(object? sender, MessageEventArgs e)
        {
            var message = SocketMessage.ReadMessage(e.RawData);
            if(message is PushMessage pushMessage)
            {
                var pushType = CoreUtils.GetEntity(pushMessage.EntityType);
                var pushObject = Serialization.Deserialize(pushType, pushMessage.EntityData);
                ClientFactory.PushHandlers.Push(pushType, pushObject);
            }
            else if(message is InitialMessage)
            {
            }
        }

        private void Socket_OnOpen(object? sender, EventArgs e)
        {
            Logger.Send(LogType.Information, "", "WebSocket connected to server");

            var initial = new InitialMessage(Session, ClientFactory.Platform);
            Socket.Send(initial.WriteToBytes());
        }

        private void Socket_OnClose(object? sender, CloseEventArgs e)
        {
            Logger.Send(LogType.Information, "", "WebSocket disconnected from server");
            if (!Closed)
            {
                Task.Run(() =>
                {
                    if (!Socket.IsAlive)
                    {
                        Task.Delay(30_000).Wait(); // Try to reconnect after 30 seconds
                        Socket.Connect();
                    }
                });
            }
        }

        private void Socket_OnError(object? sender, WebSocketSharp.ErrorEventArgs e)
        {
            Logger.Send(LogType.Error, "", $"WebSocket Error: {e.Message}");
        }

        public void Dispose()
        {
            Closed = true;
            if (Socket.IsAlive)
            {
                Socket.Close();
            }
        }
    }
}
