using EVCB_OCPP.Domain;
using EVCB_OCPP.Domain.Models.Database;
using EVCB_OCPP.Packet.Messages.SubTypes;
using EVCB_OCPP.WSServer.Dto;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NLog;
using SuperSocket.SocketBase;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace EVCB_OCPP.WSServer.Service
{
    internal class CPOOuterResponse
    {
        public CPOOuterResponse()
        {
            StatusCode = 0;

        }

        public int StatusCode { set; get; }

        public string StatusMessage { set; get; }

        public string Data { set; get; }

        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
        public string SerialNo { set; get; }


        [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
        public string ErrorDetail { set; get; }


    }
    internal class CustomerSignMaterial
    {
        internal bool CallsThirdParty { set; get; }

        internal string Id { set; get; }

        internal string APIUrl { set; get; }

        internal string SaltKey { set; get; }
    }
    public class OuterBusinessService : IBusinessService
    {

        private readonly ILogger<OuterBusinessService> logger;
        private readonly IDbContextFactory<MainDBContext> maindbContextFactory;
        private readonly IMainDbService mainDbService;
        private readonly OuterHttpClient httpClient;

        private string _CustomerId = string.Empty;
        private CustomerSignMaterial signMaterial = null;

        public string CustomerId
        {
            get => _CustomerId;
            set
            {
                _CustomerId = value;
                signMaterial = GetSign(_CustomerId).Result;
            }
        }

        internal CustomerSignMaterial CustomerSignMaterial
        {
            get => signMaterial;
            set
            {
                signMaterial = value;
                _CustomerId = signMaterial.Id;
            }
        }

        public OuterBusinessService(
            ILogger<OuterBusinessService> logger,
            IDbContextFactory<MainDBContext> maindbContextFactory,
            IMainDbService mainDbService,
            OuterHttpClient httpClient)
        {
            this.logger = logger;
            this.maindbContextFactory = maindbContextFactory;
            this.mainDbService = mainDbService;
            this.httpClient = httpClient;
        }


        async public Task<IdTokenInfo> Authorize(string chargeBoxId, string idTag, int? connectorId = null)
        {
            //return new IdTokenInfo() { IdTagInfo = new IdTagInfo()
            //{
            //    expiryDate = DateTime.UtcNow.AddDays(1),
            //    status = AuthorizationStatus.Accepted 
            //} };

            //await Task.Delay(10);
            IdTokenInfo result = new IdTokenInfo() { IdTagInfo = new IdTagInfo() { status = AuthorizationStatus.Invalid } };

            try
            {
                logger.LogInformation(chargeBoxId + " Charging Monitor======================================>");

                string requestParams = idTag.StartsWith("vid:") ? await GetRequestParamsAsPnC(chargeBoxId, idTag, connectorId) : GetRequestParamsAsNormal(chargeBoxId, idTag);

                HttpResult response = await httpClient.Post(signMaterial.APIUrl + requestParams, new Dictionary<string, string>()
                            {
                                { "PartnerId",signMaterial.Id}
                            }, requestBody: null, saltkey: signMaterial.SaltKey).ConfigureAwait(false);
                logger.LogInformation($"{chargeBoxId} response : {JsonConvert.SerializeObject(response)}");

                if (response.Success)
                {
                    //Console.WriteLine(response.Response);
                    var _httpResult = JsonConvert.DeserializeObject<CPOOuterResponse>(response.Response);
                    JObject jo = JObject.Parse(_httpResult.Data);

                    if (jo.ContainsKey("ExpiryDate"))
                    {
                        DateTime dt = jo["ExpiryDate"].Value<DateTime>();
                        result.IdTagInfo.expiryDate = dt;
                    }

                    if (jo.ContainsKey("ParentIdTag"))
                    {
                        string _Message = jo["ParentIdTag"].Value<string>();
                        result.IdTagInfo.parentIdTag = _Message;

                    }

                    if (jo.ContainsKey("ChargePointFee"))
                    {
                       

                        for(int i=0;i< jo["ChargePointFee"].Count();i++)
                        {
                            if(i==0)
                            {
                                result.ChargePointFee = new List<ChargePointFee>();
                            }
                            result.ChargePointFee.Add(jo["ChargePointFee"][i].ToObject<ChargePointFee>());
                        }
                     
                    }

                    if (jo.ContainsKey("ChargepointFee"))
                    {                       

                        for (int i = 0; i < jo["ChargepointFee"].Count(); i++)
                        {
                            if (i == 0)
                            {
                                result.ChargePointFee = new List<ChargePointFee>();
                            }

                            result.ChargePointFee.Add(jo["ChargepointFee"][i].ToObject<ChargePointFee>());
                        }

                    }

                    if (jo.ContainsKey("AccountBalance"))
                    {
                        decimal accountBalance = jo["AccountBalance"].Value<decimal>();
                        result.AccountBalance = accountBalance;
                    }


                    if (jo.ContainsKey("Status"))
                    {
                        string _Message = jo["Status"].Value<string>();
                        result.IdTagInfo.status = (AuthorizationStatus)Enum.Parse(typeof(AuthorizationStatus), _Message);
                    }
                }
                else
                {
                    logger.LogError(chargeBoxId + " OuterBusinessService.Authorize Fail: " + response.Response);
                }

            }
            catch (Exception ex)
            {
                result.IdTagInfo.status = AuthorizationStatus.Invalid;

                logger.LogError(chargeBoxId + " OuterBusinessService.Authorize Ex: " + ex.ToString());
            }

            return result;

        }

        async public Task NotifyFaultStatus(ErrorDetails details)
        {

            try
            {
                if (signMaterial.CallsThirdParty)
                {
                    var response = await httpClient.Post(signMaterial.APIUrl + "connectorfault", new Dictionary<string, string>()
                            {
                                { "PartnerId",signMaterial.Id}

                            }, details, signMaterial.SaltKey).ConfigureAwait(false);
                }
            }
            catch (Exception ex)
            {

                logger.LogError(details.ChargeBoxId + " OuterBusinessService.NotifyFaultStatus Ex: " + ex.ToString());
            }


        }

        async public Task NotifyConnectorUnplugged(string chargeBoxId, string data)
        {
            try
            {
                JObject jo = JObject.Parse(data);

                var details = new { ChargeBoxId = chargeBoxId, SessionId = jo["idTx"].Value<Int32>(), Timestamp = jo["timestamp"].Value<DateTime>() };
                if (signMaterial.CallsThirdParty)
                {
                    var response = await httpClient.Post(signMaterial.APIUrl + "connectorunplugged", new Dictionary<string, string>()
                            {
                                { "PartnerId",signMaterial.Id}

                            }, details, signMaterial.SaltKey).ConfigureAwait(false);


                }


            }
            catch (Exception ex)
            {

                logger.LogError(chargeBoxId + " OuterBusinessService.NotifyConnectorUnplugged Ex: " + ex.ToString());
            }

        }

        private async Task<CustomerSignMaterial> GetSign(string customerId)
        {
            Guid Id = new Guid(customerId);
            CustomerSignMaterial _customer = new CustomerSignMaterial();


            //using (var db = new MainDBContext())
            //using (var db = maindbContextFactory.CreateDbContextAsync())
            //{
            //    _customer = await db.Customer.Where(x => x.Id == Id).Select(x => new CustomerSignMaterial() { Id = x.Id.ToString(), APIUrl = x.ApiUrl, SaltKey = x.ApiKey, CallsThirdParty = x.CallPartnerApiOnSchedule }).FirstOrDefaultAsync();
            //}
            var _customerDb = await mainDbService.GetCustomer(Id);
            if (_customerDb is not null)
            {
                _customer.Id = _customerDb.Id.ToString();
                _customer.APIUrl = _customerDb.ApiUrl;
                _customer.SaltKey = _customerDb.ApiKey;
                _customer.CallsThirdParty = _customerDb.CallPartnerApiOnSchedule;
            }
            return _customer;
        }

        private async ValueTask<string> GetRequestParamsAsPnC(string chargeBoxId, string idTag, int? connectorId)
        {
            idTag = idTag.Replace("vid:", "");

            if (connectorId is null)
            {
                using (var db = await maindbContextFactory.CreateDbContextAsync())
                {
                    var connectorStatuses = await db.ConnectorStatus.Where(x => x.ChargeBoxId == chargeBoxId).
                         Select(x => new { x.ConnectorId, x.Status, x.CreatedOn }).ToListAsync();

                    var connectorStatus = connectorStatuses.Where(x => x.Status == 2).OrderByDescending(x => x.CreatedOn).FirstOrDefault();
                    if (connectorStatus != null)
                    {
                        connectorId = connectorStatus.ConnectorId;
                    }
                }
            }

            return string.Format("charging_auth?ChargeBoxId={0}&ConnectorId={1}&IdTag={2}", chargeBoxId, connectorId, idTag);
        }

        private string GetRequestParamsAsNormal(string chargeBoxId, string idTag)
        {
            return string.Format("charging_auth?ChargeBoxId={0}&IdTag={1}", chargeBoxId, idTag);
        }

        public Task NotifyConnectorUnplugged(string data)
        {
            throw new NotImplementedException();
        }
    }
}