using Dapper;
using EVCB_OCPP.Domain;
using EVCB_OCPP.Domain.Models.Database;
using EVCB_OCPP.Packet.Features;
using EVCB_OCPP.Packet.Messages;
using EVCB_OCPP.Packet.Messages.Core;
using EVCB_OCPP.Packet.Messages.SubTypes;
using EVCB_OCPP.WSServer.Dto;
using EVCB_OCPP.WSServer.Service;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NLog;
using OCPPPackage.Profiles;
using OCPPServer.Protocol;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;

namespace EVCB_OCPP.WSServer.Message
{
    public class ID_CreditDeductResult
    {
        public int txId { set; get; }

        public string creditNo { set; get; }


        public bool deductResult { set; get; }

        public bool isDonateInvoice { set; get; }


        public decimal amount { set; get; }

        public string approvalNo { set; get; }

    }

    public class ID_ReaderStatus
    {
        public int ConnectorId { set; get; }

        public string creditNo { set; get; }


        public string SerialNo { set; get; }

        public int readerStatus { set; get; }

        public string VEMData { set; get; }

        public DateTime Timestamp { set; get; }




    }

    internal partial class ProfileHandler
    {
        static private ILogger logger = NLog.LogManager.GetCurrentClassLogger();
        string webConnectionString = ConfigurationManager.ConnectionStrings["WebDBContext"].ConnectionString;
        private OuterHttpClient httpClient = new OuterHttpClient();
        async internal Task<MessageResult> ExecuteCoreRequest(Actions action, ClientData session, IRequest request)
        {
            Stopwatch watch = new Stopwatch();
            if (action == Actions.Heartbeat)
            {
                watch.Start();
            }
            MessageResult result = new MessageResult() { Success = false };

            try
            {
                switch (action)
                {
                    case Actions.DataTransfer:
                        {
                            DataTransferRequest _request = request as DataTransferRequest;
                            var confirm = new DataTransferConfirmation() { status = DataTransferStatus.UnknownMessageId };

                            if (_request.messageId == "ID_CreditDeductResult")
                            {

                                var creditDeductResult = JsonConvert.DeserializeObject<ID_CreditDeductResult>(_request.data);
                                if (session.CustomerId == new Guid("009E603C-79CD-4620-A2B8-D9349C0E8AD8"))
                                {
                                    var report = new
                                    {
                                        ChargeBoxId = session.ChargeBoxId,
                                        IsDonateInvoice = creditDeductResult.isDonateInvoice,
                                        CreditNo = creditDeductResult.creditNo,
                                        DeductResult = creditDeductResult.deductResult,
                                        SessionId = creditDeductResult.txId,
                                        ApprovalNo = creditDeductResult.approvalNo,
                                        TotalCost = creditDeductResult.amount,

                                    };

                                    var response = await httpClient.Post(GlobalConfig.TCC_API_URL + "prepare_issue_invoice", new Dictionary<string, string>()
                                        {
                                            { "PartnerId",session.CustomerId.ToString()}

                                        }, report, GlobalConfig.TCC_SALTKEY);

                                    logger.Debug(JsonConvert.SerializeObject(response));
                                }


                                confirm.status = DataTransferStatus.Accepted;
                                confirm.data = JsonConvert.SerializeObject(new { txId = creditDeductResult.txId, creditNo = creditDeductResult.creditNo, msgId = _request.messageId });
                            }
                            if (_request.messageId == "ID_ReaderStatus")
                            {
                                if (session.CustomerId == new Guid("009E603C-79CD-4620-A2B8-D9349C0E8AD8"))
                                {
                                    var preauth_status = JsonConvert.DeserializeObject<ID_ReaderStatus>(_request.data);
                                    var report = new
                                    {
                                        ChargeBoxId = session.ChargeBoxId,
                                        ConnectorId = preauth_status.ConnectorId,
                                        CreditNo = preauth_status.creditNo,
                                        ReaderStatus = preauth_status.readerStatus,
                                        SerialNo = preauth_status.SerialNo,
                                        VEMData = preauth_status.VEMData,
                                        Timestamp = preauth_status.Timestamp

                                    };

                                    var response = await httpClient.Post(GlobalConfig.TCC_API_URL + "preauth_status", new Dictionary<string, string>()
                                        {
                                            { "PartnerId",session.CustomerId.ToString()}

                                        }, report, GlobalConfig.TCC_SALTKEY);


                                    confirm.status = DataTransferStatus.Accepted;
                                }
                            }
                            result.Message = confirm;
                            result.Success = true;
                        }
                        break;
                    case Actions.BootNotification:
                        {
                            BootNotificationRequest _request = request as BootNotificationRequest;
                            int heartbeat_interval = GlobalConfig.GetHEARTBEAT_INTERVAL();
                            using (var db = new MainDBContext())
                            {
                                var _machine = db.Machine.Where(x => x.ChargeBoxId == session.ChargeBoxId).FirstOrDefault();
                                _machine.ChargeBoxSerialNumber = string.IsNullOrEmpty(_request.chargeBoxSerialNumber) ? string.Empty : _request.chargeBoxSerialNumber;
                                _machine.ChargePointSerialNumber = string.IsNullOrEmpty(_request.chargePointSerialNumber) ? string.Empty : _request.chargePointSerialNumber;
                                _machine.ChargePointModel = string.IsNullOrEmpty(_request.chargePointModel) ? string.Empty : _request.chargePointModel;
                                _machine.ChargePointVendor = string.IsNullOrEmpty(_request.chargePointVendor) ? string.Empty : _request.chargePointVendor;
                                _machine.FW_CurrentVersion = string.IsNullOrEmpty(_request.firmwareVersion) ? string.Empty : _request.firmwareVersion;
                                //_machine.Iccid = string.IsNullOrEmpty(_request.iccid) ? string.Empty : _request.iccid;
                                _machine.Iccid = DateTime.UtcNow.ToString("yy-MM-dd HH:mm");
                                _machine.Imsi = string.IsNullOrEmpty(_request.imsi) ? string.Empty : _request.imsi;
                                _machine.MeterSerialNumber = string.IsNullOrEmpty(_request.meterSerialNumber) ? string.Empty : _request.meterSerialNumber;
                                _machine.MeterType = string.IsNullOrEmpty(_request.meterType) ? string.Empty : _request.meterType;

                                db.SaveChanges();



                                var configVaule = db.MachineConfiguration.Where(x => x.ChargeBoxId == session.ChargeBoxId && x.ConfigureName == StandardConfiguration.HeartbeatInterval)
                                    .Select(x => x.ConfigureSetting).FirstOrDefault();

                                if (configVaule != null)
                                {
                                    int.TryParse(configVaule, out heartbeat_interval);
                                }
                            }
                            var confirm = new BootNotificationConfirmation() { currentTime = DateTime.UtcNow, interval = session.IsPending ? heartbeat_interval : 5, status = session.IsPending ? Packet.Messages.SubTypes.RegistrationStatus.Accepted : RegistrationStatus.Pending };

                            session.IsPending = !session.IsPending;
                            result.Message = confirm;
                            result.Success = true;
                        }
                        break;
                    case Actions.StatusNotification:
                        {
                            //只保留最新上報狀況
                            StatusNotificationRequest _request = request as StatusNotificationRequest;
                            int preStatus = 0;
                            using (var db = new MainDBContext())
                            {
                                var _oldStatus = db.ConnectorStatus.Where(x => x.ChargeBoxId == session.ChargeBoxId
                              && x.ConnectorId == _request.connectorId).AsNoTracking().FirstOrDefault();


                                if (_oldStatus != null && (_request.status != (ChargePointStatus)_oldStatus.Status || _request.status == ChargePointStatus.Faulted))
                                {

                                    preStatus = _oldStatus.Status;

                                    db.Configuration.AutoDetectChangesEnabled = false;
                                    db.Configuration.ValidateOnSaveEnabled = false;
                                    db.ConnectorStatus.Attach(_oldStatus);


                                    _oldStatus.CreatedOn = _request.timestamp.HasValue ? _request.timestamp.Value : DateTime.UtcNow;
                                    _oldStatus.Status = (int)_request.status;
                                    _oldStatus.ChargePointErrorCodeId = (int)_request.errorCode;
                                    _oldStatus.ErrorInfo = string.IsNullOrEmpty(_request.info) ? string.Empty : _request.info;
                                    _oldStatus.VendorId = string.IsNullOrEmpty(_request.vendorId) ? string.Empty : _request.vendorId;
                                    _oldStatus.VendorErrorCode = string.IsNullOrEmpty(_request.vendorErrorCode) ? string.Empty : _request.vendorErrorCode;


                                    db.Entry(_oldStatus).Property(x => x.CreatedOn).IsModified = true;
                                    db.Entry(_oldStatus).Property(x => x.Status).IsModified = true;
                                    db.Entry(_oldStatus).Property(x => x.ChargePointErrorCodeId).IsModified = true;
                                    db.Entry(_oldStatus).Property(x => x.ErrorInfo).IsModified = true;
                                    db.Entry(_oldStatus).Property(x => x.VendorId).IsModified = true;
                                    db.Entry(_oldStatus).Property(x => x.VendorErrorCode).IsModified = true;


                                }

                                if (_oldStatus == null)
                                {
                                    var _currentStatus = new Domain.Models.Database.ConnectorStatus()
                                    {
                                        ChargeBoxId = session.ChargeBoxId,
                                        ConnectorId = (byte)_request.connectorId,
                                        CreatedOn = _request.timestamp.HasValue ? _request.timestamp.Value : DateTime.UtcNow,
                                        Status = (int)_request.status,
                                        ChargePointErrorCodeId = (int)_request.errorCode,
                                        ErrorInfo = string.IsNullOrEmpty(_request.info) ? string.Empty : _request.info,
                                        VendorId = string.IsNullOrEmpty(_request.vendorId) ? string.Empty : _request.vendorId,
                                        VendorErrorCode = string.IsNullOrEmpty(_request.vendorErrorCode) ? string.Empty : _request.vendorErrorCode,
                                        Id = Guid.NewGuid().ToString()
                                    };
                                    db.ConnectorStatus.Add(_currentStatus);

                                }
                                if (_request.status == Packet.Messages.SubTypes.ChargePointStatus.Faulted)
                                {
                                    db.MachineError.Add(new MachineError()
                                    {
                                        ConnectorId = (byte)_request.connectorId,
                                        CreatedOn = _request.timestamp.HasValue ? _request.timestamp.Value : DateTime.UtcNow,
                                        Status = (int)_request.status,
                                        ChargeBoxId = session.ChargeBoxId,
                                        ErrorCodeId = (int)_request.errorCode,
                                        ErrorInfo = string.IsNullOrEmpty(_request.info) ? string.Empty : _request.info,
                                        PreStatus = _oldStatus == null ? -1 : preStatus,
                                        VendorErrorCode = string.IsNullOrEmpty(_request.vendorErrorCode) ? string.Empty : _request.vendorErrorCode,
                                        VendorId = string.IsNullOrEmpty(_request.vendorId) ? string.Empty : _request.vendorId
                                    });
                                }
                                db.SaveChanges();
                            }


                            if (_request.status == Packet.Messages.SubTypes.ChargePointStatus.Faulted)
                            {
                                var businessService = BusinessServiceFactory.CreateBusinessService(session.CustomerId.ToString());
                                var notification = businessService.NotifyFaultStatus(new ErrorDetails()
                                {
                                    ChargeBoxId = session.ChargeBoxId,
                                    ConnectorId = _request.connectorId,
                                    ErrorCode = _request.errorCode,
                                    Info = string.IsNullOrEmpty(_request.info) ? string.Empty : _request.info,
                                    OCcuredOn = _request.timestamp ?? DateTime.UtcNow,
                                    VendorErrorCode = string.IsNullOrEmpty(_request.vendorErrorCode) ? string.Empty : _request.vendorErrorCode,

                                });
                            }

                            var confirm = new StatusNotificationConfirmation() { };
                            result.Message = confirm;
                            result.Success = true;
                        }
                        break;
                    case Actions.Heartbeat:
                        {

                            var confirm = new HeartbeatConfirmation() { currentTime = DateTime.UtcNow };
                            result.Message = confirm;
                            result.Success = true;
                        }
                        break;
                    case Actions.MeterValues:
                        {

                            MeterValuesRequest _request = request as MeterValuesRequest;
                            decimal energy_kwh = 0;

                            if (_request.meterValue.Count > 0)
                            {


                                using (var db = new MeterValueDBContext())
                                {
                                    foreach (var item in _request.meterValue)
                                    {
                                        int registerCount = item.sampledValue.Where(x => x.measurand == Measurand.Energy_Active_Import_Interval).ToList().Count;
                                        if (registerCount > 0)
                                        {
                                            registerCount = 0;
                                        }
                                        else
                                        {
                                            registerCount = item.sampledValue.Where(x => x.measurand == Measurand.Energy_Active_Import_Register).ToList().Count;
                                        }

                                        if (registerCount > 0)
                                        {
                                            string voltage_text = item.sampledValue.Where(x => x.measurand == Measurand.Voltage).Select(x => x.value).FirstOrDefault();
                                            decimal voltage = decimal.Parse(voltage_text);
                                            string current_text = item.sampledValue.Where(x => x.measurand == Measurand.Current_Import).Select(x => x.value).FirstOrDefault();
                                            decimal current = decimal.Parse(current_text);
                                            decimal meterStart = 0;
                                            string energyRegister_text = item.sampledValue.Where(x => x.measurand == Measurand.Energy_Active_Import_Register).Select(x => x.value).FirstOrDefault();
                                            decimal energyRegister = decimal.Parse(energyRegister_text);
                                            using (var maindb = new MainDBContext())
                                            {
                                                meterStart = maindb.TransactionRecord.Where(x => x.Id == _request.transactionId.Value).Select(x => x.MeterStart).FirstOrDefault();
                                            }

                                            item.sampledValue.Add(new SampledValue()
                                            {
                                                context = ReadingContext.Sample_Periodic,
                                                format = ValueFormat.Raw,
                                                location = Location.Outlet,
                                                phase = Phase.L1_N,
                                                unit = UnitOfMeasure.kWh,
                                                measurand = Measurand.Energy_Active_Import_Interval,
                                                value = decimal.Divide(decimal.Subtract(energyRegister, meterStart), (decimal)1000).ToString()
                                            });
                                            item.sampledValue.Add(new SampledValue()
                                            {
                                                context = ReadingContext.Sample_Periodic,
                                                format = ValueFormat.Raw,
                                                location = Location.Outlet,
                                                phase = Phase.L1_N,
                                                unit = UnitOfMeasure.kW,
                                                measurand = Measurand.Power_Active_Import,
                                                value = decimal.Divide(decimal.Multiply(voltage, current), (decimal)1000).ToString()
                                            });
                                        }
                                        foreach (var sampleVaule in item.sampledValue)
                                        {
                                            decimal value = Convert.ToDecimal(sampleVaule.value);

                                            if (sampleVaule.context == ReadingContext.Sample_Periodic && sampleVaule.measurand == Measurand.Energy_Active_Import_Interval)
                                            {
                                                energy_kwh = sampleVaule.unit == UnitOfMeasure.Wh ? Decimal.Divide(value, 1000) : value;
                                            }

                                            string sp = "[dbo].[uspInsertMeterValueRecord] @ChargeBoxId," +
                         "@ConnectorId,@Value,@CreatedOn,@ContextId,@FormatId,@MeasurandId,@PhaseId,@LocationId,@UnitId,@TransactionId";

                                            List<SqlParameter> parameter = new List<SqlParameter>
                                        {
                                              new SqlParameter("ChargeBoxId",session.ChargeBoxId),
                                              new SqlParameter("ConnectorId",  (byte)_request.connectorId),
                                              new SqlParameter("Value",value),
                                              new SqlParameter("CreatedOn",item.timestamp),
                                              new SqlParameter("ContextId",sampleVaule.context.HasValue ? (int)sampleVaule.context : 0),
                                              new SqlParameter("FormatId",sampleVaule.format.HasValue ? (int)sampleVaule.format : 0),
                                              new SqlParameter("MeasurandId",sampleVaule.measurand.HasValue ? (int)sampleVaule.measurand : 0),
                                              new SqlParameter("PhaseId",sampleVaule.phase.HasValue ? (int)sampleVaule.phase : 0),
                                              new SqlParameter("LocationId",sampleVaule.location.HasValue ? (int)sampleVaule.location : 0),
                                              new SqlParameter("UnitId",sampleVaule.unit.HasValue ? (int)sampleVaule.unit : 0),
                                              new SqlParameter("TransactionId",_request.transactionId.HasValue?_request.transactionId:-1),
                                          };


                                            db.Database.ExecuteSqlCommand(sp, parameter.ToArray());




                                        }

                                    }
                                }
                            }

                            //  if (energy_kwh > 0)
                            {
                                try
                                {
                                    if (session.IsBilling)
                                        if (session.IsBilling)
                                        {
                                            using (var db = new MainDBContext())
                                            {
                                                db.ServerMessage.Add(new ServerMessage()
                                                {
                                                    ChargeBoxId = session.ChargeBoxId,
                                                    CreatedBy = "Server",
                                                    CreatedOn = DateTime.UtcNow,
                                                    OutAction = Actions.DataTransfer.ToString(),
                                                    OutRequest = JsonConvert.SerializeObject(
                                                            new DataTransferRequest()
                                                            {
                                                                messageId = "ID_TxEnergy",
                                                                vendorId = "Phihong Technology",
                                                                data = JsonConvert.SerializeObject(new { txId = _request.transactionId, ConnectorId = _request.connectorId })
                                                            },
                                                            new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
                                                    SerialNo = Guid.NewGuid().ToString(),
                                                    InMessage = string.Empty

                                                });

                                                db.SaveChanges();
                                            }
                                        }


                                }
                                catch (Exception ex)
                                {

                                    Console.WriteLine(string.Format("{0} :{1}", session.ChargeBoxId + " RunningCost", ex.Message));

                                }

                            }

                            var confirm = new MeterValuesConfirmation() { };
                            result.Message = confirm;
                            result.Success = true;
                        }
                        break;
                    case Actions.StartTransaction:
                        {

                            StartTransactionRequest _request = request as StartTransactionRequest;

                            int _transactionId = -1;

                            var businessService = BusinessServiceFactory.CreateBusinessService(session.CustomerId.ToString());

                            var _idTagInfo = new IdTagInfo() { expiryDate = DateTime.UtcNow.AddDays(1), status = AuthorizationStatus.Accepted };
                            if (_request.idTag != "Backend")
                            {
                                var authorization_result = await businessService.Authorize(session.ChargeBoxId, _request.idTag);
                                _idTagInfo = authorization_result.IdTagInfo;

                                if (authorization_result.ChargePointFee != null)
                                {
                                    var price = authorization_result.ChargePointFee.Where(x => x.IsAC == session.IsAC).First();
                                    if (price != null)
                                    {

                                        if (session.UserPrices.ContainsKey(_request.idTag))
                                        {
                                            session.UserPrices[_request.idTag] = price.PerkWhFee.HasValue ? JsonConvert.SerializeObject(new List<ChargingPrice>() { new ChargingPrice() { StartTime = "00:00", EndTime = "23:59", Fee = price.PerkWhFee.Value } }) : price.PerHourFee.Value.ToString();
                                            session.UserPrices[_request.idTag] += "|+" + authorization_result.AccountBalance + "+" + "&" + price.ParkingFee + "&|" + price.Currency;

                                        }
                                        else
                                        {
                                            session.UserPrices.Add(_request.idTag, price.PerkWhFee.HasValue ? JsonConvert.SerializeObject(new List<ChargingPrice>() { new ChargingPrice() { StartTime = "00:00", EndTime = "23:59", Fee = price.PerkWhFee.Value } }) : price.PerHourFee.Value.ToString());
                                            session.UserPrices[_request.idTag] += "|+" + authorization_result.AccountBalance + "+" + "&" + price.ParkingFee + "&|" + price.Currency;

                                        }
                                    }
                                }

                            }

                            //特例****飛宏客戶旗下的電樁,若遇到Portal沒回應的狀況 ~允許充電
                            if (session.CustomerId.ToString().ToUpper() == "8456AED9-6DD9-4BF3-A94C-9F5DCB9506F7" && _idTagInfo.status == AuthorizationStatus.ConcurrentTx)
                            {
                                _idTagInfo = new IdTagInfo() { expiryDate = DateTime.UtcNow.AddDays(1), status = AuthorizationStatus.Accepted };
                            }

                            string accountBalance = "0";
                            if (session.CustomerId.ToString().ToUpper() == "10C7F5BD-C89A-4E2A-8611-B617E0B41A73")
                            {
                                using (SqlConnection conn = new SqlConnection(webConnectionString))
                                {
                                    var parameters = new DynamicParameters();
                                    parameters.Add("@IdTag", _request.idTag, DbType.String, ParameterDirection.Input);
                                    string strSql = "select parentIdTag from [dbo].[LocalListDetail]  where ListId = 27 and IdTag=@IdTag; ";
                                    accountBalance = conn.ExecuteScalar<string>(strSql, parameters);
                                }
                            }


                            using (var db = new MainDBContext())
                            {
                                var _CustomerId = db.Machine.Where(x => x.ChargeBoxId == session.ChargeBoxId).Include(x => x.Customer).
                                     Select(x => x.CustomerId).FirstOrDefault();

                                var _existedTx = db.TransactionRecord.Where(x => x.CustomerId == _CustomerId && x.ChargeBoxId == session.ChargeBoxId
                                   && x.ConnectorId == _request.connectorId && x.StartTime == _request.timestamp).Select(C => new { C.Id }).AsNoTracking().FirstOrDefault();
                                TransactionRecord _newTransaction = new TransactionRecord();


                                if (_existedTx == null)
                                {
                                    _newTransaction = new TransactionRecord()
                                    {
                                        ChargeBoxId = session.ChargeBoxId,
                                        ConnectorId = (byte)_request.connectorId,
                                        CreatedOn = DateTime.UtcNow,
                                        StartIdTag = _request.idTag,
                                        MeterStart = _request.meterStart,
                                        CustomerId = _CustomerId,
                                        StartTime = _request.timestamp.ToUniversalTime(),
                                        ReservationId = _request.reservationId.HasValue ? _request.reservationId.Value : 0,
                                    };

                                    if (session.UserPrices.ContainsKey(_request.idTag))
                                    {
                                        _newTransaction.Fee = !session.IsBilling ? string.Empty : session.UserPrices[_request.idTag];

                                    }
                                    else
                                    {
                                        _newTransaction.Fee = !session.IsBilling ? string.Empty : session.BillingMethod == 1 ? JsonConvert.SerializeObject(session.ChargingPrices) : session.ChargingFeebyHour.ToString();
                                        _newTransaction.Fee += !session.IsBilling ? string.Empty : "|+" + accountBalance + "+" + "&" + session.ParkingFee + "&|" + session.Currency;
                                    }

                                    db.TransactionRecord.Add(_newTransaction);

                                    db.SaveChanges();

                                    _transactionId = _newTransaction.Id;
                                    logger.Info("***************************************************** ");
                                    logger.Info(string.Format("{0} :TransactionId {1} ", session.ChargeBoxId, _newTransaction.Id));
                                    logger.Info("***************************************************** ");
                                }
                                else
                                {
                                    _transactionId = _existedTx.Id;
                                    logger.Error("Duplication ***************************************************** " + _existedTx.Id);
                                }
                            }


                            var confirm = new StartTransactionConfirmation()
                            {
                                idTagInfo = _idTagInfo,
                                transactionId = _transactionId
                            };


                            result.Message = confirm;
                            result.Success = true;
                        }
                        break;
                    case Actions.StopTransaction:
                        {
                            StopTransactionRequest _request = request as StopTransactionRequest;

                            int _ConnectorId = 0;

                            var businessService = BusinessServiceFactory.CreateBusinessService(session.CustomerId.ToString());

                            var _idTagInfo = string.IsNullOrEmpty(_request.idTag) ? null : (_request.idTag == "Backend" ?
                                new IdTagInfo() { expiryDate = DateTime.UtcNow.AddDays(1), status = AuthorizationStatus.Accepted } : (await businessService.Authorize(session.ChargeBoxId, _request.idTag)).IdTagInfo);

                            //特例****飛宏客戶旗下的電樁,若遇到Portal沒回應的狀況 ~允許充電
                            if (session.CustomerId.ToString().ToUpper() == "8456AED9-6DD9-4BF3-A94C-9F5DCB9506F7" && _idTagInfo != null && _idTagInfo.status == AuthorizationStatus.ConcurrentTx)
                            {
                                _idTagInfo = new IdTagInfo() { expiryDate = DateTime.UtcNow.AddDays(1), status = AuthorizationStatus.Accepted };
                            }

                            using (var db = new MainDBContext())
                            {
                                var transaction = db.TransactionRecord.Where(x => x.Id == _request.transactionId
                                 && x.ChargeBoxId == session.ChargeBoxId).FirstOrDefault();

                                //遠傳太久以前的停止充電 直接拒絕 避免電樁持續重送~~~~~~~
                                if (_request.timestamp < new DateTime(2021, 11, 1))
                                {
                                    var confirm = new StopTransactionConfirmation()
                                    {
                                        idTagInfo = new IdTagInfo()
                                        {
                                            status = AuthorizationStatus.Invalid
                                        }

                                    };

                                    result.Message = confirm;
                                    result.Success = true;
                                    return result;
                                }


                                if (transaction != null)
                                {
                                    _ConnectorId = transaction.ConnectorId;
                                    transaction.MeterStop = _request.meterStop;
                                    transaction.StopTime = _request.timestamp.ToUniversalTime();
                                    transaction.StopReasonId = _request.reason.HasValue ? (int)_request.reason.Value : 0;
                                    transaction.StopReason = _request.reason.HasValue ? _request.reason.Value.ToString() : Reason.Local.ToString();
                                    transaction.StopIdTag = _request.idTag;
                                    transaction.Receipt = string.Empty;
                                    transaction.Cost = session.IsBilling ? -1 : 0;


                                    await db.SaveChangesAsync();
                                    var confirm = new StopTransactionConfirmation()
                                    {
                                        idTagInfo = _idTagInfo

                                    };


                                    if (session.IsBilling)
                                    {
                                        db.ServerMessage.Add(new ServerMessage()
                                        {
                                            ChargeBoxId = session.ChargeBoxId,
                                            CreatedBy = "Server",
                                            CreatedOn = DateTime.UtcNow,
                                            OutAction = Actions.DataTransfer.ToString(),
                                            OutRequest = JsonConvert.SerializeObject(
                                                       new DataTransferRequest()
                                                       {
                                                           messageId = "ID_TxEnergy",
                                                           vendorId = "Phihong Technology",
                                                           data = JsonConvert.SerializeObject(new { txId = _request.transactionId, ConnectorId = transaction.ConnectorId })
                                                       },
                                                       new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
                                            SerialNo = Guid.NewGuid().ToString(),
                                            InMessage = string.Empty

                                        });

                                        db.SaveChanges();
                                    }

                                    result.Message = confirm;
                                    result.Success = true;
                                }
                                else
                                {

                                    result.Exception = new Exception("Can't find transactionId " + _request.transactionId);

                                }
                            }

                            #region Save MeterValue

                            if (_request.transactionData != null)
                            {
                                if (_request.transactionData.Count > 0)
                                {
                                    using (var _meterDb = new MeterValueDBContext())
                                    {
                                        foreach (var item in _request.transactionData)
                                        {
                                            foreach (var sampleVaule in item.sampledValue)
                                            {
                                                decimal value = Convert.ToDecimal(sampleVaule.value);

                                                string sp = "[dbo].[uspInsertMeterValueRecord] @ChargeBoxId," +
                             "@ConnectorId,@Value,@CreatedOn,@ContextId,@FormatId,@MeasurandId,@PhaseId,@LocationId,@UnitId,@TransactionId";

                                                List<SqlParameter> parameter = new List<SqlParameter>
                                        {
                                              new SqlParameter("ChargeBoxId",session.ChargeBoxId),
                                              new SqlParameter("ConnectorId",  (byte)_ConnectorId),
                                              new SqlParameter("Value",value),
                                              new SqlParameter("CreatedOn",item.timestamp),
                                              new SqlParameter("ContextId",sampleVaule.context.HasValue ? (int)sampleVaule.context : 0),
                                              new SqlParameter("FormatId",sampleVaule.format.HasValue ? (int)sampleVaule.format : 0),
                                              new SqlParameter("MeasurandId",sampleVaule.measurand.HasValue ? (int)sampleVaule.measurand : 0),
                                              new SqlParameter("PhaseId",sampleVaule.phase.HasValue ? (int)sampleVaule.phase : 0),
                                              new SqlParameter("LocationId",sampleVaule.location.HasValue ? (int)sampleVaule.location : 0),
                                              new SqlParameter("UnitId",sampleVaule.unit.HasValue ? (int)sampleVaule.unit : 0),
                                              new SqlParameter("TransactionId",_request.transactionId),
                                          };


                                                _meterDb.Database.ExecuteSqlCommand(sp, parameter.ToArray());
                                            }
                                        }
                                    }


                                }
                            }

                            #endregion

                        }
                        break;
                    case Actions.Authorize:
                        {
                            AuthorizeRequest _request = request as AuthorizeRequest;

                            var businessService = BusinessServiceFactory.CreateBusinessService(session.CustomerId.ToString());
                            var confirm = new AuthorizeConfirmation()
                            {
                                idTagInfo = new IdTagInfo() { expiryDate = DateTime.UtcNow.AddDays(1), status = AuthorizationStatus.Accepted }
                            };
                            if (_request.idTag != "Backend")
                            {
                                var authorization_result = await businessService.Authorize(session.ChargeBoxId, _request.idTag);
                                confirm.idTagInfo = authorization_result.IdTagInfo;

                                if (authorization_result.ChargePointFee != null)
                                {
                                    var price = authorization_result.ChargePointFee.Where(x => x.IsAC == session.IsAC).First();
                                    if (price != null)
                                    {

                                        if (session.UserPrices.ContainsKey(_request.idTag))
                                        {
                                            session.UserPrices[_request.idTag] = price.PerkWhFee.HasValue ? JsonConvert.SerializeObject(new List<ChargingPrice>() { new ChargingPrice() { StartTime = "00:00", EndTime = "23:59", Fee = price.PerkWhFee.Value } }) : price.PerHourFee.Value.ToString();
                                            session.UserPrices[_request.idTag] += "|+" + authorization_result.AccountBalance + "+" + "&" + price.ParkingFee + "&|" + price.Currency;

                                        }
                                        else
                                        {
                                            session.UserPrices.Add(_request.idTag, price.PerkWhFee.HasValue ? JsonConvert.SerializeObject(new List<ChargingPrice>() { new ChargingPrice() { StartTime = "00:00", EndTime = "23:59", Fee = price.PerkWhFee.Value } }) : price.PerHourFee.Value.ToString());
                                            session.UserPrices[_request.idTag] += "|+" + authorization_result.AccountBalance + "+" + "&" + price.ParkingFee + "&|" + price.Currency;

                                        }

                                        if (session.UserDisplayPrices.ContainsKey(_request.idTag))
                                        {
                                            session.UserDisplayPrices[_request.idTag] = price.DisplayMessage;
                                        }
                                        else
                                        {
                                            session.UserDisplayPrices.Add(_request.idTag, price.DisplayMessage);
                                        }
                                    }
                                }

                            }
                            //特例****飛宏客戶旗下的電樁,若遇到Portal沒回應的狀況 ~允許充電
                            if (session.CustomerId.ToString().ToUpper() == "8456AED9-6DD9-4BF3-A94C-9F5DCB9506F7" && confirm.idTagInfo.status == AuthorizationStatus.ConcurrentTx)
                            {
                                confirm.idTagInfo = new IdTagInfo() { expiryDate = DateTime.UtcNow.AddDays(1), status = AuthorizationStatus.Accepted };
                            }

                            result.Message = confirm;
                            result.Success = true;
                        }
                        break;
                    default:
                        {
                            Console.WriteLine(string.Format("Not Implement {0} Logic(ExecuteCoreRequest)", request.GetType().ToString().Replace("OCPPPackage.Messages.Core.", "")));
                        }
                        break;
                }
            }
            catch (Exception ex)
            {
                logger.Fatal(string.Format("chargeBoxId:{0} {1}", session.ChargeBoxId, action));
                logger.Fatal(string.Format("Data {0}", request.ToString()));
                logger.Fatal(string.Format("Error {0}", ex.ToString()));
                result.Exception = ex;
            }


            if (action == Actions.Heartbeat)
            {
                watch.Stop();
                if (watch.ElapsedMilliseconds / 1000 > 3)
                {
                    logger.Error("Processing Hearbeat costs " + watch.ElapsedMilliseconds / 1000 + " seconds");
                }
            }
            return result;
        }

        async internal Task<MessageResult> ExecuteCoreConfirm(Actions action, ClientData session, IConfirmation confirm, string requestId)
        {
            MessageResult result = new MessageResult() { Success = true };

            try
            {
                switch (action)
                {

                    case Actions.DataTransfer:
                        {
                            DataTransferConfirmation _confirm = confirm as DataTransferConfirmation;
                            DataTransferRequest _request = _confirm.GetRequest() as DataTransferRequest;
                            using (var db = new MainDBContext())
                            {
                                var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
                                x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
                                if (operation != null)
                                {
                                    operation.FinishedOn = DateTime.UtcNow;
                                    operation.Status = 1;//電樁有回覆
                                    operation.EVSE_Status = (int)_confirm.status;
                                    operation.EVSE_Value = string.IsNullOrEmpty(_confirm.data) ? "" : _confirm.data;
                                    await db.SaveChangesAsync();
                                }

                                if (_request.messageId == "ID_FirmwareVersion")
                                {
                                    var machine = new Machine() { Id = session.MachineId };
                                    if (machine != null)
                                    {
                                        db.Configuration.AutoDetectChangesEnabled = false;
                                        db.Configuration.ValidateOnSaveEnabled = false;
                                        db.Machine.Attach(machine);
                                        machine.BoardVersions = _confirm.data;
                                        db.Entry(machine).Property(x => x.BoardVersions).IsModified = true;
                                        await db.SaveChangesAsync();
                                    }
                                }

                                if (_request.messageId == "ID_TxEnergy") //計費
                                {
                                    if (_confirm.status == DataTransferStatus.Accepted)
                                    {
                                        decimal couponPoint = 0m;
                                        string receipt = string.Empty;
                                        List<ChargingBill> bill = new List<ChargingBill>();
                                        List<ChargingPrice> chargingPrices = new List<ChargingPrice>();
                                        var txEnergy = JsonConvert.DeserializeObject<TransactionEnergy>(_confirm.data);
                                        var feedto = db.TransactionRecord.Where(x => x.Id == txEnergy.TxId).Select(x => new { Id = x.Id, ConnectorId = x.ConnectorId, Fee = x.Fee, StopTime = x.StopTime, StartTime = x.StartTime }).FirstOrDefault();
                                        decimal chargedEnergy = 0m;
                                        if (feedto == null || string.IsNullOrEmpty(feedto.Fee)) return result;
                                        string currency = feedto.Fee.Substring(feedto.Fee.Length - 3);
                                        decimal chargingCost = 0;
                                        if (feedto.Fee.Length > 58)
                                        {
                                            chargingPrices = JsonConvert.DeserializeObject<List<ChargingPrice>>(feedto.Fee.Split('|')[0]);
                                            foreach (var item in txEnergy.PeriodEnergy)
                                            {
                                                DateTime dt = new DateTime(2021, 01, 01, int.Parse(item.Key), 0, 0, DateTimeKind.Utc);
                                                string startTime = dt.ToString("hh:mm tt", new CultureInfo("en-us"));
                                                decimal perfee = 0;

                                                //小數點無條件捨去到第4位
                                                var periodEnergy = (decimal)((int)Decimal.Multiply(item.Value, 10000) / (double)10000);
                                                chargedEnergy += periodEnergy;
                                                if (chargingPrices.Count == 1)
                                                {
                                                    perfee = Decimal.Multiply(periodEnergy, chargingPrices[0].Fee);
                                                    if (bill.Count == 0)
                                                    {
                                                        bill.Add(new ChargingBill()
                                                        {
                                                            StartTime = "12:00 AM",
                                                            EndTime = "11:59 PM",
                                                            Fee = chargingPrices[0].Fee
                                                        });
                                                    }

                                                    bill[0].PeriodEnergy += periodEnergy;

                                                }
                                                else
                                                {
                                                    var price = chargingPrices.Where(x => x.StartTime == startTime).FirstOrDefault();
                                                    perfee = Decimal.Multiply(periodEnergy, price.Fee);

                                                    bill.Add(new ChargingBill()
                                                    {
                                                        StartTime = price.StartTime,
                                                        EndTime = price.EndTime,
                                                        PeriodEnergy = periodEnergy,
                                                        Fee = price.Fee,

                                                    });
                                                }
                                                if (bill.Count > 0)
                                                {
                                                    bill[bill.Count - 1].Total += DollarRounding(perfee, session.Currency);
                                                    chargingCost += bill[bill.Count - 1].Total;

                                                    if (bill.Count == 1)
                                                    {

                                                        bill[bill.Count - 1].Total = DollarRounding(Decimal.Multiply(bill[0].PeriodEnergy, bill[0].Fee), session.Currency);
                                                        chargingCost = bill[bill.Count - 1].Total;
                                                    }
                                                }
                                            }
                                        }
                                        else
                                        {
                                            //以小時計費
                                            foreach (var item in txEnergy.PeriodEnergy)
                                            {  //小數點無條件捨去到第4位
                                                var periodEnergy = (decimal)((int)Decimal.Multiply(item.Value, 10000) / (double)10000);
                                                chargedEnergy += periodEnergy;
                                            }

                                            var fee = decimal.Parse(feedto.Fee.Split('|')[0]);
                                            var charging_stoptime = feedto.StopTime == GlobalConfig.DefaultNullTime ? DateTime.Parse(DateTime.UtcNow.ToString("yyyy/MM/dd HH:mm")) : DateTime.Parse(feedto.StopTime.ToString("yyyy/MM/dd HH:mm"));
                                            var charging_starttime = DateTime.Parse(feedto.StartTime.ToString("yyyy/MM/dd HH:mm"));
                                            chargingCost = Decimal.Multiply((decimal)charging_stoptime.Subtract(charging_starttime).TotalHours, fee);
                                            chargingCost = DollarRounding(chargingCost, session.Currency);
                                        }

                                        // 計算停車費
                                        var parkingFee = decimal.Parse(feedto.Fee.Split('&')[1]);
                                        var stoptime = feedto.StopTime == GlobalConfig.DefaultNullTime ? DateTime.Parse(DateTime.UtcNow.ToString("yyyy/MM/dd HH:mm")) : DateTime.Parse(feedto.StopTime.ToString("yyyy/MM/dd HH:mm"));
                                        var starttime = DateTime.Parse(feedto.StartTime.ToString("yyyy/MM/dd HH:mm"));
                                        var totalHours = stoptime.Subtract(starttime).TotalHours;
                                        var parkingCost = Decimal.Multiply((decimal)totalHours, parkingFee);
                                        parkingCost = DollarRounding(parkingCost, session.Currency);

                                        if (feedto.StopTime != GlobalConfig.DefaultNullTime)
                                        {
                                            var customerInfo = db.Customer.Where(x => x.Id == session.CustomerId).Select(x => new { x.InstantStopTxReport, x.ApiUrl, x.ApiKey }).FirstOrDefault();

                                            decimal accountBalance = 0;
                                            decimal.TryParse(feedto.Fee.Split('+')[1], out accountBalance);

                                            var tx = db.TransactionRecord.Where(x => x.Id == txEnergy.TxId).FirstOrDefault();
                                            if (tx == null)
                                            {
                                                Console.WriteLine("Tx is empty");
                                                return result;
                                            }

                                            if (tx.BillingDone) return result;


                                            var startTime = new DateTime(tx.StartTime.Year, tx.StartTime.Month, tx.StartTime.Day, tx.StartTime.Hour, 0, 0);
                                            List<ChargingBill> confirmbill = new List<ChargingBill>();
                                            receipt = string.Format("({0} )Energy:", chargedEnergy);

                                            while (startTime < tx.StopTime)
                                            {
                                                if (bill.Count == 1)
                                                {
                                                    confirmbill = bill;
                                                    receipt += string.Format("| {0} - {1}:| {2} kWh @ ${3}/kWh=${4}", tx.StartTime.ToString("hh:mm tt", new CultureInfo("en-us")), tx.StopTime.ToString("hh:mm tt", new CultureInfo("en-us")),
                                                        confirmbill[0].PeriodEnergy.ToString("0.0000"), bill[0].Fee, bill[0].Total);

                                                    break;
                                                }
                                                if (bill.Count == 0)
                                                {
                                                    receipt += string.Format("| {0} - {1} @ ${2}/hr=${3}", feedto.StartTime.ToString("hh:mm tt", new CultureInfo("en-us")),
                                                       feedto.StopTime.ToString("hh:mm tt", new CultureInfo("en-us")), feedto.Fee.Split('|')[0], chargingCost);

                                                    break;
                                                }
                                                if (bill.Count > 1)
                                                {
                                                    var time = startTime.ToString("hh:mm tt", new CultureInfo("en-us"));
                                                    var tt = bill.Where(x => x.StartTime == time).FirstOrDefault();
                                                    confirmbill.Add(tt);
                                                    if (confirmbill.Count == 1)
                                                    {
                                                        confirmbill[0].StartTime = tx.StartTime.ToString("hh:mm tt", new CultureInfo("en-us"));
                                                    }


                                                    var stopTimeText = tx.StopTime.ToString("hh:mm tt", new CultureInfo("en-us"));
                                                    if (confirmbill[confirmbill.Count - 1].StartTime.Contains(stopTimeText.Split(' ')[1]))
                                                    {
                                                        var subHourText = (int.Parse(stopTimeText.Split(':')[0])).ToString();
                                                        subHourText = subHourText.Length == 1 ? "0" + subHourText : subHourText;
                                                        if (confirmbill[confirmbill.Count - 1].StartTime.Contains(subHourText))
                                                        {
                                                            confirmbill[confirmbill.Count - 1].EndTime = stopTimeText;
                                                        }

                                                    }
                                                    receipt += string.Format("| {0} - {1}:| {2} kWh @ ${3}/kWh=${4}", confirmbill[confirmbill.Count - 1].StartTime, confirmbill[confirmbill.Count - 1].EndTime,
                                                        confirmbill[confirmbill.Count - 1].PeriodEnergy.ToString("0.0000"), confirmbill[confirmbill.Count - 1].Fee, confirmbill[confirmbill.Count - 1].Total);

                                                    if (confirmbill.Count == 24) break;

                                                }
                                                startTime = startTime.AddHours(1);

                                            }

                                            if (session.CustomerId == new Guid("009E603C-79CD-4620-A2B8-D9349C0E8AD8"))
                                            {
                                                chargingCost = confirmbill.Count > 0 ? confirmbill.Sum(x => x.Total) : chargingCost;
                                                chargingCost = chargedEnergy == 0 ? 0 : (chargingCost + parkingCost < 1 ? 1 : chargingCost);  //台泥最低一元
                                                receipt += string.Format("|Total Energy Fee : ${0}", chargingCost);

                                                receipt += string.Format("|Parking Fee: | {0} - {1} @ ${2}/hr=${3}", feedto.StartTime.ToString("hh:mm tt", new CultureInfo("en-us")),
                                                feedto.StopTime.ToString("hh:mm tt", new CultureInfo("en-us")), parkingFee, parkingCost);
                                                tx.Cost = chargingCost + parkingCost;
                                            }
                                            else
                                            {
                                                chargingCost = confirmbill.Count > 0 ? confirmbill.Sum(x => x.Total) : chargingCost;
                                                receipt += string.Format("|Total Energy Fee : ${0}", chargingCost);

                                                receipt += string.Format("|Parking Fee: | {0} - {1} @ ${2}/hr=${3}", feedto.StartTime.ToString("hh:mm tt", new CultureInfo("en-us")),
                                                feedto.StopTime.ToString("hh:mm tt", new CultureInfo("en-us")), parkingFee, parkingCost);
                                                tx.Cost = chargingCost + parkingCost;
                                            }


                                            if (customerInfo != null && customerInfo.InstantStopTxReport)
                                            {

                                                var request = new
                                                {
                                                    ChargeBoxId = tx.ChargeBoxId,
                                                    ConnectorId = tx.ConnectorId,
                                                    SessionId = tx.Id,
                                                    MeterStart = tx.MeterStart,
                                                    MeterStop = tx.MeterStop,
                                                    IdTag = tx.StartIdTag,
                                                    StartTime = tx.StartTime.ToString(GlobalConfig.UTC_DATETIMEFORMAT),
                                                    StopTime = tx.StopTime.ToString(GlobalConfig.UTC_DATETIMEFORMAT),
                                                    StopReason = tx.StopReasonId < 1 ? "Unknown" : (tx.StopReasonId > 12 ? "Unknown" : ((Reason)tx.StopReasonId).ToString()),
                                                    Receipt = tx.Receipt,
                                                    TotalCost = tx.Cost,
                                                    Fee = tx.Fee

                                                };

                                                var response = await httpClient.Post(customerInfo.ApiUrl + "completed_session", new Dictionary<string, string>()
                                                {
                                                    { "PartnerId",session.CustomerId.ToString()}

                                                }, request, customerInfo.ApiKey);

                                                var _httpResult = JsonConvert.DeserializeObject<CPOOuterResponse>(response.Response);
                                                JObject jo = JObject.Parse(_httpResult.Data);
                                                if (jo.ContainsKey("CouponPoint"))
                                                {
                                                    couponPoint = jo["CouponPoint"].Value<Decimal>();

                                                }

                                                if (jo.ContainsKey("FarewellMessage"))
                                                {
                                                    string _Message = jo["FarewellMessage"].Value<string>();


                                                }
                                            }

                                            tx.Receipt = receipt;
                                            tx.BillingDone = true;
                                            db.Configuration.AutoDetectChangesEnabled = false;
                                            db.Configuration.ValidateOnSaveEnabled = false;
                                            db.TransactionRecord.Attach(tx);
                                            db.Entry(tx).Property(x => x.Cost).IsModified = true;
                                            db.Entry(tx).Property(x => x.Receipt).IsModified = true;
                                            db.Entry(tx).Property(x => x.BillingDone).IsModified = true;

                                            db.ServerMessage.Add(new ServerMessage()
                                            {
                                                ChargeBoxId = session.ChargeBoxId,
                                                CreatedBy = "Server",
                                                CreatedOn = DateTime.UtcNow,
                                                OutAction = Actions.DataTransfer.ToString(),
                                                OutRequest = JsonConvert.SerializeObject(
                                                            new DataTransferRequest()
                                                            {
                                                                messageId = "FinalCost",
                                                                vendorId = "Phihong Technology",
                                                                data = JsonConvert.SerializeObject(new
                                                                {
                                                                    txId = txEnergy.TxId,
                                                                    description = JsonConvert.SerializeObject(new
                                                                    {
                                                                        chargedEnergy = chargedEnergy,
                                                                        chargingFee = chargingCost,
                                                                        parkTime = (int)stoptime.Subtract(starttime).TotalSeconds,
                                                                        parkingFee = parkingCost,
                                                                        currency = currency,
                                                                        couponPoint = couponPoint,
                                                                        accountBalance = accountBalance - tx.Cost
                                                                    })
                                                                })

                                                            },
                                                            new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
                                                SerialNo = Guid.NewGuid().ToString(),
                                                InMessage = string.Empty

                                            }); ;

                                            await db.SaveChangesAsync();

                                            using (var meterdb = new MeterValueDBContext())
                                            {
                                                string sp = "[dbo].[uspInsertMeterValueRecord] @ChargeBoxId," +
                                   "@ConnectorId,@Value,@CreatedOn,@ContextId,@FormatId,@MeasurandId,@PhaseId,@LocationId,@UnitId,@TransactionId";

                                                List<SqlParameter> parameter = new List<SqlParameter>
                                                {
                                                      new SqlParameter("ChargeBoxId",session.ChargeBoxId),
                                                      new SqlParameter("ConnectorId",  (byte)feedto.ConnectorId),
                                                      new SqlParameter("Value",chargingCost),
                                                      new SqlParameter("CreatedOn",DateTime.UtcNow),
                                                      new SqlParameter("ContextId",(int)ReadingContext.Sample_Periodic),
                                                      new SqlParameter("FormatId",(int)ValueFormat.Raw),
                                                      new SqlParameter("MeasurandId",(int)Measurand.TotalCost),
                                                      new SqlParameter("PhaseId", -1),
                                                      new SqlParameter("LocationId", -1),
                                                      new SqlParameter("UnitId", -1),
                                                      new SqlParameter("TransactionId",feedto.Id),
                                                };


                                                meterdb.Database.ExecuteSqlCommand(sp, parameter.ToArray());
                                            }

                                            using (SqlConnection conn = new SqlConnection(webConnectionString))
                                            {
                                                var parameters = new DynamicParameters();
                                                parameters.Add("@IdTag", tx.StartIdTag, DbType.String, ParameterDirection.Input);
                                                parameters.Add("@parentIdTag", accountBalance - tx.Cost, DbType.String, ParameterDirection.Input);
                                                string strSql = "update [dbo].[LocalListDetail] set parentIdTag =@parentIdTag  where ListId = 27 and IdTag=@IdTag; ";
                                                await conn.ExecuteAsync(strSql, parameters);

                                            }

                                            #region 提供給PHA 過CDFA認證 使用
                                            if (tx.CustomerId == Guid.Parse("10C7F5BD-C89A-4E2A-8611-B617E0B41A73"))
                                            {
                                                var mail_response = new OuterHttpClient().PostFormDataAsync("http://ocpp.phihong.com.tw/CDFA/" + tx.Id, new Dictionary<string, string>()
                                                {
                                                    { "email","2"},
                                                    { "to","wonderj@phihongusa.com;jessica_tseng@phihong.com.tw"}
                                                    //{ "to","jessica_tseng@phihong.com.tw"}
                                               }, null);

                                                Console.WriteLine(JsonConvert.SerializeObject(mail_response));

                                            }
                                            #endregion

                                        }
                                        else
                                        {
                                            db.ServerMessage.Add(new ServerMessage()
                                            {
                                                ChargeBoxId = session.ChargeBoxId,
                                                CreatedBy = "Server",
                                                CreatedOn = DateTime.UtcNow,
                                                OutAction = Actions.DataTransfer.ToString(),
                                                OutRequest = JsonConvert.SerializeObject(
                                                 new DataTransferRequest()
                                                 {
                                                     messageId = "RunningCost",
                                                     vendorId = "Phihong Technology",
                                                     data = JsonConvert.SerializeObject(new
                                                     {
                                                         txId = txEnergy.TxId,
                                                         description = JsonConvert.SerializeObject(new
                                                         {
                                                             chargedEnergy = chargedEnergy,
                                                             chargingFee = chargingCost,
                                                             parkTime = (int)stoptime.Subtract(starttime).TotalSeconds,
                                                             parkingFee = parkingCost,
                                                             currency = currency
                                                         })

                                                     })

                                                 },
                                                 new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore, Formatting = Formatting.None }),
                                                SerialNo = Guid.NewGuid().ToString(),
                                                InMessage = string.Empty

                                            }); ;

                                            await db.SaveChangesAsync();

                                            using (var meterdb = new MeterValueDBContext())
                                            {
                                                string sp = "[dbo].[uspInsertMeterValueRecord] @ChargeBoxId," +
                       "@ConnectorId,@Value,@CreatedOn,@ContextId,@FormatId,@MeasurandId,@PhaseId,@LocationId,@UnitId,@TransactionId";

                                                List<SqlParameter> parameter = new List<SqlParameter>
                                                {
                                                      new SqlParameter("ChargeBoxId",session.ChargeBoxId),
                                                      new SqlParameter("ConnectorId",  (byte)feedto.ConnectorId),
                                                      new SqlParameter("Value",chargingCost),
                                                      new SqlParameter("CreatedOn",DateTime.UtcNow),
                                                      new SqlParameter("ContextId",(int)ReadingContext.Sample_Periodic),
                                                      new SqlParameter("FormatId",(int)ValueFormat.Raw),
                                                      new SqlParameter("MeasurandId",(int)Measurand.ChargingCost),
                                                      new SqlParameter("PhaseId", -1),
                                                      new SqlParameter("LocationId", -1),
                                                      new SqlParameter("UnitId", -1),
                                                      new SqlParameter("TransactionId",feedto.Id),
                                                };


                                                meterdb.Database.ExecuteSqlCommand(sp, parameter.ToArray());
                                            }

                                        }


                                    }

                                }

                                #region 台泥
                                if (_request.messageId == "ID_GetTxUserInfo")
                                {
                                    var txUserInfo = JsonConvert.DeserializeObject<ID_GetTxUserInfo>(_confirm.data);
                                    if (session.CustomerId == new Guid("009E603C-79CD-4620-A2B8-D9349C0E8AD8"))
                                    {
                                        var request = new
                                        {
                                            ChargeBoxId = session.ChargeBoxId,
                                            ConnectorId = txUserInfo.ConnectorId,
                                            SessionId = txUserInfo.TxId,
                                            SerialNo = txUserInfo.SerialNo,
                                            StartTime = txUserInfo.StartTime.ToString(GlobalConfig.UTC_DATETIMEFORMAT),
                                            VEMData = txUserInfo.VEMData
                                        };

                                        var response = httpClient.Post(GlobalConfig.TCC_API_URL + "start_session", new Dictionary<string, string>()
                                        {
                                            { "PartnerId",session.CustomerId.ToString()}

                                        }, request, GlobalConfig.TCC_SALTKEY);

                                        logger.Debug(JsonConvert.SerializeObject(response));
                                    }

                                }

                                #endregion
                            }
                        }
                        break;
                    case Actions.ChangeAvailability:
                        {
                            ChangeAvailabilityConfirmation _confirm = confirm as ChangeAvailabilityConfirmation;
                            ChangeAvailabilityRequest _request = _confirm.GetRequest() as ChangeAvailabilityRequest;

                            using (var db = new MainDBContext())
                            {
                                var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
                                x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
                                if (operation != null)
                                {
                                    operation.FinishedOn = DateTime.UtcNow;
                                    operation.Status = 1;//電樁有回覆
                                    operation.EVSE_Status = (int)_confirm.status;
                                    operation.EVSE_Value = _confirm.status.ToString();
                                    await db.SaveChangesAsync();
                                }

                            }
                        }
                        break;

                    case Actions.ClearCache:
                        {
                            ClearCacheConfirmation _confirm = confirm as ClearCacheConfirmation;
                            ClearCacheRequest _request = _confirm.GetRequest() as ClearCacheRequest;

                            using (var db = new MainDBContext())
                            {
                                var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
                                x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
                                if (operation != null)
                                {
                                    operation.FinishedOn = DateTime.UtcNow;
                                    operation.Status = 1;//電樁有回覆
                                    operation.EVSE_Status = (int)_confirm.status;
                                    operation.EVSE_Value = _confirm.status.ToString();
                                    await db.SaveChangesAsync();
                                }

                            }
                        }
                        break;
                    case Actions.RemoteStartTransaction:
                        {
                            RemoteStartTransactionConfirmation _confirm = confirm as RemoteStartTransactionConfirmation;
                            RemoteStartTransactionRequest _request = _confirm.GetRequest() as RemoteStartTransactionRequest;

                            using (var db = new MainDBContext())
                            {
                                var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
                                x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
                                if (operation != null)
                                {
                                    operation.FinishedOn = DateTime.UtcNow;
                                    operation.Status = 1;//電樁有回覆
                                    operation.EVSE_Status = (int)_confirm.status;
                                    operation.EVSE_Value = _confirm.status.ToString();
                                    await db.SaveChangesAsync();
                                }

                            }
                        }
                        break;
                    case Actions.RemoteStopTransaction:
                        {
                            RemoteStopTransactionConfirmation _confirm = confirm as RemoteStopTransactionConfirmation;
                            RemoteStopTransactionRequest _request = _confirm.GetRequest() as RemoteStopTransactionRequest;

                            using (var db = new MainDBContext())
                            {
                                var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
                                x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
                                if (operation != null)
                                {
                                    operation.FinishedOn = DateTime.UtcNow;
                                    operation.Status = 1;//電樁有回覆
                                    operation.EVSE_Status = (int)_confirm.status;
                                    operation.EVSE_Value = _confirm.status.ToString();
                                    await db.SaveChangesAsync();
                                }

                            }
                        }
                        break;
                    case Actions.Reset:
                        {
                            ResetConfirmation _confirm = confirm as ResetConfirmation;
                            ResetRequest _request = _confirm.GetRequest() as ResetRequest;

                            using (var db = new MainDBContext())
                            {
                                var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
                                x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
                                if (operation != null)
                                {
                                    operation.FinishedOn = DateTime.UtcNow;
                                    operation.Status = 1;//電樁有回覆
                                    operation.EVSE_Status = (int)_confirm.status;
                                    operation.EVSE_Value = _confirm.status.ToString();
                                    await db.SaveChangesAsync();
                                }

                            }
                        }
                        break;
                    case Actions.ChangeConfiguration:
                        {
                            ChangeConfigurationConfirmation _confirm = confirm as ChangeConfigurationConfirmation;
                            ChangeConfigurationRequest _request = _confirm.GetRequest() as ChangeConfigurationRequest;

                            using (var db = new MainDBContext())
                            {
                                var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
                         x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();

                                if (operation != null)
                                {
                                    operation.FinishedOn = DateTime.UtcNow;
                                    operation.Status = 1;//電樁有回覆
                                    operation.EVSE_Status = (int)_confirm.status;
                                    operation.EVSE_Value = _confirm.status.ToString();

                                }

                                if (_confirm.status == Packet.Messages.SubTypes.ConfigurationStatus.Accepted || _confirm.status == Packet.Messages.SubTypes.ConfigurationStatus.RebootRequired)
                                {
                                    var configure = db.MachineConfiguration.Where(x => x.ChargeBoxId == session.ChargeBoxId).ToList();

                                    var foundConfig = configure.Find(x => x.ConfigureName == _request.key);
                                    if (foundConfig != null)
                                    {
                                        foundConfig.ReadOnly = false;
                                        foundConfig.ConfigureSetting = _request.value;
                                    }
                                    else
                                    {
                                        db.MachineConfiguration.Add(new MachineConfiguration()
                                        {
                                            ChargeBoxId = session.ChargeBoxId,
                                            ConfigureName = _request.key,
                                            ReadOnly = false,
                                            ConfigureSetting = _request.value
                                        });
                                    }
                                }
                                await db.SaveChangesAsync();
                            }

                        }
                        break;
                    case Actions.GetConfiguration:
                        {

                            try
                            {
                                GetConfigurationConfirmation _confirm = confirm as GetConfigurationConfirmation;
                                //  GetConfigurationRequest _request = _confirm.GetRequest() as GetConfigurationRequest;

                                using (var db = new MainDBContext())
                                {
                                    var configure = db.MachineConfiguration.Where(x => x.ChargeBoxId == session.ChargeBoxId).ToList();

                                    if (_confirm.configurationKey != null)
                                    {
                                        foreach (var item in _confirm.configurationKey)
                                        {
                                            string oldValue = string.Empty;
                                            if (item.key == null)
                                            {
                                                Console.WriteLine("*********************");
                                            }
                                            var foundConfig = configure.Find(x => x.ConfigureName == item.key);


                                            if (foundConfig != null)
                                            {
                                                if (foundConfig.ConfigureName == null)
                                                {
                                                    Console.WriteLine("*********************");
                                                }

                                                if (foundConfig.ConfigureName == "SecurityProfile")
                                                {
                                                    oldValue = foundConfig.ConfigureSetting;
                                                }

                                                foundConfig.ReadOnly = item.IsReadOnly;
                                                foundConfig.ConfigureSetting = string.IsNullOrEmpty(item.value) ? string.Empty : item.value;
                                            }
                                            else
                                            {
                                                db.MachineConfiguration.Add(new MachineConfiguration()
                                                {
                                                    ChargeBoxId = session.ChargeBoxId,
                                                    ConfigureName = item.key,
                                                    ReadOnly = item.IsReadOnly,
                                                    ConfigureSetting = string.IsNullOrEmpty(item.value) ? string.Empty : item.value,
                                                    Exists = true
                                                });
                                            }


                                        }
                                    }
                                    if (_confirm.unknownKey != null)
                                    {

                                        foreach (var item in _confirm.unknownKey)
                                        {
                                            var foundConfig = configure.Find(x => x.ConfigureName == item);
                                            if (foundConfig != null)
                                            {
                                                foundConfig.ReadOnly = true;
                                                foundConfig.ConfigureSetting = string.Empty;
                                                foundConfig.Exists = false;
                                            }
                                            else
                                            {
                                                db.MachineConfiguration.Add(new MachineConfiguration()
                                                {
                                                    ChargeBoxId = session.ChargeBoxId,
                                                    ConfigureName = item
                                                });
                                            }
                                        }
                                    }

                                    var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
                                   x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();

                                    if (operation != null)
                                    {
                                        operation.FinishedOn = DateTime.UtcNow;
                                        operation.Status = 1;//電樁有回覆
                                        operation.EVSE_Status = 1;
                                        operation.EVSE_Value = JsonConvert.SerializeObject(_confirm.configurationKey, Formatting.None);

                                    }

                                    await db.SaveChangesAsync();

                                }
                            }
                            catch (Exception ex)
                            {
                                logger.Error(ex.ToString());
                            }

                        }
                        break;
                    case Actions.UnlockConnector:
                        {
                            UnlockConnectorConfirmation _confirm = confirm as UnlockConnectorConfirmation;
                            UnlockConnectorRequest _request = _confirm.GetRequest() as UnlockConnectorRequest;

                            using (var db = new MainDBContext())
                            {
                                var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
                                x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
                                if (operation != null)
                                {
                                    operation.FinishedOn = DateTime.UtcNow;
                                    operation.Status = 1;//電樁有回覆
                                    operation.EVSE_Status = (int)_confirm.status;
                                    operation.EVSE_Value = _confirm.status.ToString();
                                    await db.SaveChangesAsync();
                                }

                            }
                        }
                        break;
                    default:
                        {
                            Console.WriteLine(string.Format("Not Implement {0} Logic(ExecuteCoreConfirm)", confirm.GetType().ToString().Replace("OCPPPackage.Messages.Core.", "")));
                        }
                        break;
                }
            }
            catch (Exception ex)
            {
                logger.Debug("123 " + action + " " + ex.ToString());
            }



            return result;
        }


        internal MessageResult ReceivedCoreError(Actions action, string errorMsg, ClientData session, string requestId)
        {
            MessageResult result = new MessageResult() { Success = true };

            switch (action)
            {
                case Actions.ChangeAvailability:
                case Actions.ChangeConfiguration:
                case Actions.ClearCache:
                case Actions.RemoteStartTransaction:
                case Actions.RemoteStopTransaction:
                case Actions.Reset:
                case Actions.GetConfiguration:
                case Actions.UnlockConnector:
                case Actions.DataTransfer:
                    {
                        if (action == Actions.DataTransfer)
                        {
                            logger.Debug(string.Format("DataTransfer Error {0}: {1}", session.ChargeBoxId, requestId));
                        }

                        using (var db = new MainDBContext())
                        {
                            var operation = db.MachineOperateRecord.Where(x => x.SerialNo == requestId &&
                            x.ChargeBoxId == session.ChargeBoxId && x.Status == 0).FirstOrDefault();
                            if (operation != null)
                            {
                                operation.FinishedOn = DateTime.UtcNow;
                                operation.Status = 1;//電樁有回覆
                                operation.EVSE_Status = (int)255;//錯誤
                                operation.EVSE_Value = errorMsg;
                                db.SaveChanges();
                            }

                        }

                    }
                    break;

                default:
                    {
                        Console.WriteLine(string.Format("Not Implement {0} Logic(ReceivedCoreError)", action));
                    }
                    break;
            }
            return result;

        }


        /// <summary>
        /// 依據幣值處理4捨5入
        /// </summary>
        /// <param name="money"></param>
        /// <param name="currency"></param>
        /// <returns></returns>
        private decimal DollarRounding(decimal money, string currency)
        {


            if (currency == "USD" || currency == "EUR")
            {

                //0.4867
                if ((double)((int)(money * 100) + 0.5) <= (double)(money * 100))
                {

                    //money = Decimal.Add(money, (decimal)0.01);//0.4967

                }
                money = Math.Round(money, 2, MidpointRounding.AwayFromZero);
                money = Decimal.Parse(money.ToString("0.00"));


            }
            else
            {
                if ((double)((int)(money) + 0.5) <= (double)money)
                {
                    //  money = (int) money + 1;
                }
                money = Math.Round(money, 0, MidpointRounding.AwayFromZero);
                money = Decimal.Parse(money.ToString("0"));
            }

            return money;
        }
    }
}