using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using Fleck;
using InABox.Core;
using Logger = InABox.Core.Logger;

namespace InABox.Rpc
{
    public class RpcServerSocketTransport : RpcServerTransport<RpcServerSocketConnection2>
    {
        private WebSocketServer? _server;

        public static X509Certificate2? Certificate { get; set; }
        
        //public static void InitCertificate(string certificateFile) => Certificate = new X509Certificate2(certificateFile);
        
        public override bool IsSecure() => Certificate != null;
        
        public RpcServerSocketTransport(int port, X509Certificate2? certificate = null) 
        {
            
            Certificate = certificate;

            var protocol = certificate != null ? "wss" : "ws";

            _server = new WebSocketServer($"{protocol}://0.0.0.0:{port}");

            if (Certificate != null)
            {
                /*_server.SslConfiguration.ServerCertificate = Certificate;
                _server.SslConfiguration.ClientCertificateRequired = false;
                _server.SslConfiguration.CheckCertificateRevocation = false;
                _server.SslConfiguration.ClientCertificateValidationCallback = WSSCallback;
                _server.SslConfiguration.EnabledSslProtocols = SslProtocols.Tls12;*/
                _server.Certificate = Certificate;
            }

            /*_server?.AddWebSocketService<RpcServerSocketConnection>("/", (connection) =>
            {
                connection.Transport = this;
                //new RpcServerSocketConnection() { Transport = this };
            });*/
        }

        private bool WSSCallback(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslpolicyerrors)
        {
            return true;
        }

        public override void Start()
        {
            _server?.Start(socket =>
            {
                var connection = new RpcServerSocketConnection2(socket);
                socket.OnOpen = () => ConnectionOpened(connection);
                socket.OnClose = () => ConnectionClosed(connection);
                socket.OnError = (e) => ConnectionException(connection, e);
                socket.OnBinary = (data) => Task.Run(() =>
                {
                    RpcMessage? request = Serialization.ReadBinary<RpcMessage>(data, BinarySerializationSettings.Latest);
                    var response = DoMessage(connection, request);
                    socket.Send(Serialization.WriteBinary(response, BinarySerializationSettings.Latest));
                });
                socket.OnMessage = (data) => Task.Run(() =>
                {
                    RpcMessage? request = Serialization.Deserialize<RpcMessage>(data);
                    var response = DoMessage(connection, request);
                    socket.Send(Serialization.Serialize(response));
                });
            });
        }

        public override void Send(RpcServerSocketConnection2 connection, RpcMessage message)
        {
            connection.Connection.Send(Serialization.WriteBinary(message, BinarySerializationSettings.Latest));
        }

        public override void Stop()
        {
            _server?.ListenerSocket.Close();
        }

        public void ConnectionOpened(RpcServerSocketConnection2 connection) 
            => DoOpen(connection);

        public void ConnectionException(RpcServerSocketConnection2 connection, Exception e) 
            => DoException(connection, e);

        public void ConnectionClosed(RpcServerSocketConnection2 connection)
            => DoClose(connection, RpcTransportCloseEventType.Closed);
        
    }
}