using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using SpaceWizards.HttpListener;
using Webserver.UrlHandlers;

namespace Webserver.SSE {
    public enum ESseClientWriteResult {
        Ok,
        Closed,
        Error,
    }
    
    public class SseClient {
        private const int keepAliveIntervalSeconds = 10;
        private static readonly byte[] keepAliveData = Encoding.UTF8.GetBytes (": KeepAlive\n\n");

        private readonly SseHandler parent;
        private readonly HttpListenerResponse response;
        private DateTime lastMessageSent = DateTime.Now;

        public SseClient (SseHandler _parent, HttpListenerResponse _response) {
            parent = _parent;
            response = _response;
            
            // Keep the request open
            _response.SendChunked = true;

            _response.AddHeader ("Content-Type", "text/event-stream");
            _response.OutputStream.Flush ();
        }

        public ESseClientWriteResult Write (byte[] _bytes, int _bytesToSend) {
            HttpListenerResponse resp = response;
            try {
                if (!resp.OutputStream.CanWrite) {
                    parent.ClientClosed (this);
                    resp.Close ();
                    
                    return ESseClientWriteResult.Closed;
                }

                resp.OutputStream.Write (_bytes, 0, _bytesToSend);
                resp.OutputStream.Flush ();
                lastMessageSent = DateTime.Now;
                return ESseClientWriteResult.Ok;
            } catch (IOException e) {
                parent.ClientClosed (this);

                if (e.InnerException is SocketException se) {
                    if (se.SocketErrorCode == SocketError.ConnectionAborted || se.SocketErrorCode == SocketError.Shutdown) {
                        return ESseClientWriteResult.Closed;
                    }

                    Log.Error ($"[Web] [SSE] SocketError ({se.SocketErrorCode.ToStringCached ()}) while trying to write", true);
                    return ESseClientWriteResult.Error;
                }

                Log.Error ("[Web] [SSE] IOException while trying to write:", true);
                Log.Exception (e);
                return ESseClientWriteResult.Error;
            } catch (Exception e) {
                parent.ClientClosed (this);
                resp.Close ();

                Log.Error ("[Web] [SSE] Exception while trying to write:", true);
                Log.Exception (e);

                return ESseClientWriteResult.Error;
            }
        }

        public void HandleKeepAlive () {
            DateTime now = DateTime.Now;
            if (!((now - lastMessageSent).TotalSeconds >= keepAliveIntervalSeconds)) {
                return;
            }

            Write (keepAliveData, keepAliveData.Length);
            lastMessageSent = now;
        }
        
    }
}