using EVCB_OCPP.WSServer.Service;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using OCPPServer.Protocol;
using SuperSocket.SocketBase;
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 EVCB_OCPP.WSServer.SuperSocket;

public class OCPPWSServer : WebSocketServer<ClientData>
{

    private readonly ILogger logger;
    private readonly IConfiguration configuration;
    private readonly IMainDbService mainDbService;

    /// <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(
        IConfiguration configuration,
        IMainDbService mainDbService,
        ILogger<OCPPWSServer> logger)
        : base(new List<ISubProtocol<ClientData>>())
    {
        this.configuration = configuration;
        this.mainDbService = mainDbService;
        this.logger = logger;
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="WebSocketServer"/> class.
    /// </summary>
    /// <param name="subProtocol">The sub protocol.</param>
    //public OCPPWSServer(ISubProtocol<ClientData> subProtocol, IServiceProvider serviceProvider)
    //    : base(subProtocol)
    //{
    //    this.configuration = serviceProvider.GetService<IConfiguration>();

    //    logger = serviceProvider.GetService<ILogger<OCPPWSServer>>();
    //}

    /// <summary>
    /// Initializes a new instance of the <see cref="WebSocketServer"/> class.
    /// </summary>
    //public OCPPWSServer(IServiceProvider serviceProvider)
    //    : base(new List<ISubProtocol<ClientData>>())
    //{
    //    this.configuration = serviceProvider.GetService<IConfiguration>();

    //    logger = serviceProvider.GetService<ILogger<OCPPWSServer>>();
    //}

    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.Log();
            logger.LogWarning("===========================================");
            logger.LogWarning("session.Path EMPTY");
            logger.LogWarning("===========================================");
        }

        string[] words = session.Path.Split('/');
        session.ChargeBoxId = words.Last();

        foreach (var denyModel in GlobalConfig.DenyModelNames)
        {
            if (string.IsNullOrEmpty(denyModel)) break;
            if (session.ChargeBoxId.StartsWith(denyModel))
            {

                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);
                logger.LogTrace(sb);
                return false;
            }
        }

        if (configuration["MaintainMode"] == "1")
        {
            session.ChargeBoxId = session.ChargeBoxId + "_2";
        }

        logger.LogInformation(string.Format("ValidateHandshake: {0}", session.Path));
        bool isExistedSN = false;
        bool authorizated = false;

        var info = mainDbService.GetMachineIdAndCustomerInfo(session.ChargeBoxId).Result;
        //var machine = db.Machine.Where(x => x.ChargeBoxId == session.ChargeBoxId && x.IsDelete == false).Select(x => new { x.CustomerId, x.Id }).AsNoTracking().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;
        session.CustomerName = info.CustomerName;
        session.CustomerId = info.CustomerId;
        session.MachineId = info.MachineId;
        isExistedSN = !string.IsNullOrEmpty(info.MachineId);// machine == null ? false : true;

        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);

            logger.LogInformation(sb);
            return false;
        }

        //var configVaule = db.MachineConfigurations.Where(x => x.ChargeBoxId == session.ChargeBoxId && x.ConfigureName == StandardConfiguration.SecurityProfile)
        //                  .Select(x => x.ConfigureSetting).FirstOrDefault();
        var configVaule = mainDbService.GetMachineSecurityProfile(session.ChargeBoxId).Result;
        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);
            logger.LogInformation(sb);
            return false;
        }

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

            if (session.Items.ContainsKey("Authorization") || session.Items.ContainsKey("authorization"))
            {
                //authorizationKey = db.MachineConfigurations.Where(x => x.ChargeBoxId == session.ChargeBoxId && x.ConfigureName == StandardConfiguration.AuthorizationKey)
                //                    .Select(x => x.ConfigureSetting).FirstOrDefault();
                authorizationKey = mainDbService.GetMachineAuthorizationKey(session.ChargeBoxId).Result;

                if (session.ISOCPP20)
                {
                    // 1.6 server only support change server  function
                    securityProfile = 0;
                }

                logger.LogInformation("***********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 = Encoding.ASCII.GetString(data).Split(':');
                    logger.LogInformation("***********Authorization   " + Encoding.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);
                logger.LogInformation(sb);
                return false;
            }
        }





        logger.LogInformation(string.Format("ValidateHandshake PASS: {0}", session.Path));
        return true;
    }
}