
using EVCB_OCPP.Domain;
using NLog;
using OCPPPackage.Profiles;
using SuperWebSocket;
using SuperWebSocket.SubProtocol;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace OCPPServer.Protocol
{
    public class OCPPWSServer : WebSocketServer<ClientData>
    {

        static private ILogger logger = NLog.LogManager.GetCurrentClassLogger();
        /// <summary>
        /// 可允許連線Clinet數
        /// </summary>
        public int connectNum { get; set; }

        /// <summary>
        /// 是否限制連線Clinet數
        /// </summary>
        public bool beConnectLimit { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="WebSocketServer"/> class.
        /// </summary>
        /// <param name="subProtocols">The sub protocols.</param>
        public OCPPWSServer(IEnumerable<ISubProtocol<ClientData>> subProtocols)
            : base(subProtocols)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="WebSocketServer"/> class.
        /// </summary>
        /// <param name="subProtocol">The sub protocol.</param>
        public OCPPWSServer(ISubProtocol<ClientData> subProtocol)
            : base(subProtocol)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="WebSocketServer"/> class.
        /// </summary>
        public OCPPWSServer()
            : base(new List<ISubProtocol<ClientData>>())
        {

        }

        protected override bool ValidateClientCertificate(ClientData session, object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            //  Console.WriteLine(string.Format("{0} :{1}", session.ChargeBoxId + " ValidateClientCertificate", sslPolicyErrors));
            return true;
            // return base.ValidateClientCertificate(session, sender, certificate, chain, sslPolicyErrors);
        }

        protected override bool ValidateHandshake(ClientData session, string origin)
        {
            session.ISOCPP20 = session.SecWebSocketProtocol.ToLower().Contains("ocpp2.0");

            int securityProfile = 0;
            string authorizationKey = string.Empty;
            if (string.IsNullOrEmpty(session.Path))
            {
                logger.Warn("===========================================");
                logger.Warn("session.Path EMPTY");
                logger.Warn("===========================================");
            }

            string[] words = session.Path.Split('/');
            session.ChargeBoxId = words.Last();
            logger.Info(string.Format("ValidateHandshake: {0}", session.Path));
            bool isExistedSN = false;
            bool authorizated = false;
            using (var db = new MainDBContext())
            {
                var machine = db.Machine.Where(x => x.ChargeBoxId == session.ChargeBoxId && x.IsDelete == false).Select(x => new { x.CustomerId, x.Id }).FirstOrDefault();
                session.CustomerName = machine == null ? "Unknown" : db.Customer.Where(x => x.Id == machine.CustomerId).Select(x => x.Name).FirstOrDefault();
                session.CustomerId = machine == null ? Guid.Empty : machine.CustomerId;
                session.MachineId = machine == null ? String.Empty : machine.Id;
                isExistedSN = machine == null ? false : true;

                var configVaule = db.MachineConfiguration.Where(x => x.ChargeBoxId == session.ChargeBoxId && x.ConfigureName == StandardConfiguration.SecurityProfile)
                                  .Select(x => x.ConfigureSetting).FirstOrDefault();
                int.TryParse(configVaule, out securityProfile);

                if (session.ISOCPP20)
                {
                    // 1.6 server only support change server  function
                    securityProfile = 0;
                }
            }
            if (securityProfile == 3 && session.UriScheme == "ws")
            {
                StringBuilder responseBuilder = new StringBuilder();

                responseBuilder.AppendFormatWithCrCf(@"HTTP/{0} {1} {2}", "1.1",
                (int)HttpStatusCode.Unauthorized, @"Unauthorized");

                responseBuilder.AppendWithCrCf();
                string sb = responseBuilder.ToString();
                byte[] data = Encoding.UTF8.GetBytes(sb);

                ((IWebSocketSession)session).SendRawData(data, 0, data.Length);

                return false;
            }

            if ((securityProfile == 1 || securityProfile == 2))
            {
                if (securityProfile == 2 && session.UriScheme == "ws")
                {
                    authorizated = false;
                }

                if (session.Items.ContainsKey("Authorization") || session.Items.ContainsKey("authorization"))
                {
                   
                    using (var db = new MainDBContext())
                    {

                        authorizationKey = db.MachineConfiguration.Where(x => x.ChargeBoxId == session.ChargeBoxId && x.ConfigureName == StandardConfiguration.AuthorizationKey)
                                          .Select(x => x.ConfigureSetting).FirstOrDefault();
                      

                        if (session.ISOCPP20)
                        {
                            // 1.6 server only support change server  function
                            securityProfile = 0;
                        }
                    }
                    logger.Info("***********Authorization   ");

                    if (!string.IsNullOrEmpty(authorizationKey))
                    {
                        string base64Encoded = session.Items.ContainsKey("Authorization") ? session.Items["Authorization"].ToString().Replace("Basic ", "") : session.Items["authorization"].ToString().Replace("Basic ", "");
                        byte[] data = Convert.FromBase64String(base64Encoded);
                        string[] base64Decoded = System.Text.ASCIIEncoding.ASCII.GetString(data).Split(':');
                        logger.Info("***********Authorization   " + System.Text.ASCIIEncoding.ASCII.GetString(data));
                        if (base64Decoded.Count() == 2 && base64Decoded[0] == session.ChargeBoxId && base64Decoded[1] == authorizationKey)
                        {
                            authorizated = true;
                        }
                    }


                    
                 

                }
                else
                {
                    authorizated = true;

                }



                if (!authorizated)
                {
                    StringBuilder responseBuilder = new StringBuilder();

                    responseBuilder.AppendFormatWithCrCf(@"HTTP/{0} {1} {2}", "1.1",
                    (int)HttpStatusCode.Unauthorized, @"Unauthorized");

                    responseBuilder.AppendWithCrCf();
                    string sb = responseBuilder.ToString();
                    byte[] data = Encoding.UTF8.GetBytes(sb);

                    ((IWebSocketSession)session).SendRawData(data, 0, data.Length);

                    return false;
                }
            }


            if (!isExistedSN)
            {
                StringBuilder responseBuilder = new StringBuilder();

                responseBuilder.AppendFormatWithCrCf(@"HTTP/{0} {1} {2}", "1.1",
                (int)HttpStatusCode.NotFound, @"Not Found");

                responseBuilder.AppendWithCrCf();
                string sb = responseBuilder.ToString();
                byte[] data = Encoding.UTF8.GetBytes(sb);

                ((IWebSocketSession)session).SendRawData(data, 0, data.Length);

                return false;
            }



            return true;
        }
    }
}