using HengshanPaymentTerminal.MessageEntity.Incoming;
using HengshanPaymentTerminal.MessageEntity;
using HengshanPaymentTerminal.Support;
using HengshanPaymentTerminal;
using System;
using System.Collections.Concurrent;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Edge.Core.Processor.Dispatcher.Attributes;
using Edge.Core.IndustryStandardInterface.Pump;
using Edge.Core.IndustryStandardInterface.Pump.Fdc;
using Edge.Core.Processor;
using Edge.Core.Core.database;
using Edge.Core.Domain.FccStationInfo.Output;
using Edge.Core.Domain.FccNozzleInfo;
using Edge.Core.Domain.FccNozzleInfo.Output;
using System.Net.Sockets;
using Edge.Core.Domain.FccOrderInfo;
using Microsoft.EntityFrameworkCore;
using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities;
using static Microsoft.AspNetCore.Hosting.Internal.HostingApplication;
using HengshanPaymentTerminal.Mqtt.Request;
using HengshanPaymentTerminal.Http;
using HengshanPaymentTerminal.Http.Request;
using System.Text.Json;
using Newtonsoft.Json;
using HengshanPaymentTerminal.Http.Response;
using HengshanPaymentTerminal.MessageEntity.Outgoing;
using Microsoft.IdentityModel.Tokens;
using Org.BouncyCastle.Asn1.Ocsp;
using Newtonsoft.Json.Linq;
using System.Net;

namespace HengshanPaymentTerminal
{
    /// <summary>
    /// Handler that communicates directly with the Hengshan Payment Terminal for card handling and pump handling via serial port.
    /// </summary>
    [MetaPartsDescriptor(
        "lang-zh-cn:恒山IC卡终端（UI板） App lang-en-us:Hengshan IC card terminal (UI Board)",
        "lang-zh-cn:用于与UI板通讯控制加油机" +
        "lang-en-us:Used for terminal communication to control pumps",
        new[]
        {
            "lang-zh-cn:恒山IC卡终端lang-en-us:HengshanICTerminal"
        })]
    public class HengshanPayTermHandler : IEnumerable<IFdcPumpController>, IDeviceHandler<byte[], CommonMessage>
    {
        #region Fields

        private string pumpIds;
        private string pumpSubAddresses;
        private string pumpNozzles;
        private string pumpSiteNozzleNos;
        private string nozzleLogicIds;

        private IContext<byte[], CommonMessage> _context;
        private List<HengshanPumpHandler> pumpHandlers = new List<HengshanPumpHandler>();

        public Queue<CardMessageBase> queue = new Queue<CardMessageBase>();
        public Queue<CommonMessage> commonQueue = new Queue<CommonMessage>();
        private object syncObj = new object();

        private ConcurrentDictionary<int, PumpStateHolder> statusDict = new ConcurrentDictionary<int, PumpStateHolder>();

        public ConcurrentDictionary<int, PumpStateHolder> PumpStatusDict => statusDict;

        private Dictionary<int, int> pumpIdSubAddressDict;

        public Dictionary<int, List<int>> PumpNozzlesDict { get; private set; }

        public Dictionary<int, int> NozzleLogicIdDict { get; private set; }

        public Dictionary<int, List<int>> PumpSiteNozzleNoDict { get; private set; }

        public MysqlDbContext MysqlDbContext { get; private set; }

        public StationInfo stationInfo { get; set; }

        public List<DetailsNozzleInfoOutput> nozzleInfoList { get; private set; }

        public TcpClient? client { get; set; }

        public int? serverPort { get; set; }

        //private readonly ConcurrentDictionary<string,TaskCompletionSource<CommonMessage>> _tcsDictionary = new ConcurrentDictionary<string, TaskCompletionSource<CommonMessage>>();

        //private TaskCompletionSource<ErrorMessage> checkDisConnectTask = new TaskCompletionSource<ErrorMessage>();

        private byte frame = 0x00;
        private object lockFrame = new object();

        private IHttpClientUtil httpClientUtil;

        //记录油枪状态，key-枪号，value:01：离线  02：锁枪  03：空闲  04：提枪  06：开始加油  08：加油中
        private ConcurrentDictionary<int, int> nozzleStatusDic = new ConcurrentDictionary<int, int>(); 
        #endregion

        #region Logger

        private static NLog.Logger logger = NLog.LogManager.LoadConfiguration("NLog.config").GetLogger("IPosPlusApp");

        #endregion

        #region Constructor

        //private static List<object> ResolveCtorMetaPartsConfigCompatibility(string incompatibleCtorParamsJsonStr)
        //{
        //    var jsonParams = JsonDocument.Parse(incompatibleCtorParamsJsonStr).RootElement.EnumerateArray().ToArray();

        //    //sample:   "UITemplateVersion":"1.0"
        //    string uiTemplateVersionRegex = @"(?<=""UITemplateVersion""\:\"").+?(?="")";
        //    var match = Regex.Match(jsonParams.First().GetRawText(), uiTemplateVersionRegex, RegexOptions.IgnoreCase | RegexOptions.Multiline);

        //    if (match.Success)
        //    {
        //        var curVersion = match.Value;
        //        if (curVersion == "1.0")
        //        {
        //            var existsAppConfigV1 = JsonSerializer.Deserialize(jsonParams.First().GetRawText(), typeof(HengshanPayTerminalHanlderGroupConfigV1));
        //        }
        //        else
        //        { 
        //        }
        //    }

        //    return null;
        //}

        [ParamsJsonSchemas("TermHandlerGroupCtorParamsJsonSchemas")]
        public HengshanPayTermHandler(HengshanPayTerminalHanlderGroupConfigV2 config)
            : this(config.PumpIds,
                  string.Join(";", config.PumpSubAddresses.Select(m => $"{m.PumpId}={m.SubAddress}")),
                  string.Join(";", config.PumpNozzleLogicIds.Select(m => $"{m.PumpId}={m.LogicIds}")),
                  string.Join(";", config.PumpSiteNozzleNos.Select(m => $"{m.PumpId}={m.SiteNozzleNos}")),
                  string.Join(";", config.NozzleLogicIds.Select(m => $"{m.NozzleNo}={m.LogicId}")))
                  //clientUtil)
        {

        }

        public HengshanPayTermHandler(
            string pumpIds,
            string pumpSubAddresses,
            string pumpNozzles,
            string pumpSiteNozzleNos,
            string nozzleLogicIds)
            //IHttpClientUtil clientUtil)
        {
            this.pumpIds = pumpIds;
            this.pumpSubAddresses = pumpSubAddresses;
            this.pumpNozzles = pumpNozzles;
            this.pumpSiteNozzleNos = pumpSiteNozzleNos;
            this.nozzleLogicIds = nozzleLogicIds;
            
            AssociatedPumpIds = GetPumpIdList(pumpIds);
            pumpIdSubAddressDict = InitializePumpSubAddressMapping();
            PumpNozzlesDict = ParsePumpNozzlesList(pumpNozzles);
            PumpSiteNozzleNoDict = ParsePumpSiteNozzleNoList(pumpSiteNozzleNos);
            NozzleLogicIdDict = InitializeNozzleLogicIdMapping(nozzleLogicIds);

            InitializePumpHandlers();
        }

        #endregion

        public void OnFdcServerInit(Dictionary<string, object> parameters)
        {
            logger.Info("OnFdcServerInit called");

            if (parameters.ContainsKey("LastPriceChange"))
            {
                // nozzle logical id:rawPrice
                var lastPriceChanges = parameters["LastPriceChange"] as Dictionary<byte, int>;
                foreach (var priceChange in lastPriceChanges)
                {
                }
            }
        }

        #region Event handler

        public event EventHandler<TerminalMessageEventArgs> OnTerminalMessageReceived;

        public event EventHandler<TotalizerDataEventArgs> OnTotalizerReceived;

        public event EventHandler<FuelPriceChangeRequestEventArgs> OnFuelPriceChangeRequested;

        public event EventHandler<FuelPriceDownloadRequestedEventArgs> OnTerminalFuelPriceDownloadRequested;

        public event EventHandler<CheckCommandEventArgs> OnCheckCommandReceived;

        public event EventHandler<LockUnlockEventArgs> OnLockUnlockCompleted;

        #endregion

        #region Properties

        public List<int> AssociatedPumpIds { get; private set; }

        public IContext<byte[], CommonMessage> Context
        {
            get { return _context; }
        }

        public string PumpIdList => pumpIds;

        //public LockUnlockOperation LockUnlockOperationType { get; set; } = LockUnlockOperation.Undefined;

        #endregion

        #region Methods

        public int GetSubAddressForPump(int pumpId)
        {
            return pumpIdSubAddressDict.First(d => d.Key == pumpId).Value;
        }

        private List<int> GetPumpIdList(string pumpIds)
        {
            var pumpIdList = new List<int>();
            if (!string.IsNullOrEmpty(pumpIds) && pumpIds.Contains(',')) //multiple pumps per serial port, Hengshan TQC pump
            {
                var arr = pumpIds.Split(',');
                foreach (var item in arr)
                {
                    pumpIdList.Add(int.Parse(item));
                }
                return pumpIdList;
            }
            else if (!string.IsNullOrEmpty(pumpIds) && pumpIds.Length == 1 || pumpIds.Length == 2) //only 1 pump per serial port, Hengshan pump
            {
                return new List<int> { int.Parse(pumpIds) };
            }
            else
            {
                throw new ArgumentException("Pump id list not specified!");
            }
        }

        private Dictionary<int, int> InitializePumpSubAddressMapping()
        {
            var dict = new Dictionary<int, int>();
            if (!string.IsNullOrEmpty(pumpSubAddresses))
            {
                var sequence = pumpSubAddresses.Split(';')
                    .Select(s => s.Split('='))
                    .Select(a => new { PumpId = int.Parse(a[0]), SubAddress = int.Parse(a[1]) });

                foreach (var pair in sequence)
                {
                    if (!dict.ContainsKey(pair.PumpId))
                    {
                        dict.Add(pair.PumpId, pair.SubAddress);
                    }
                }

                return dict;
            }
            else
            {
                throw new ArgumentException("Pump id and sub address mapping does not exist");
            }
        }

        private Dictionary<int, List<int>> ParsePumpNozzlesList(string pumpNozzles)
        {
            Dictionary<int, List<int>> pumpNozzlesDict = new Dictionary<int, List<int>>();

            if (!string.IsNullOrEmpty(pumpNozzles) && pumpNozzles.Contains(';'))
            {
                var arr = pumpNozzles.Split(';');
                foreach (var subMapping in arr)
                {
                    var pair = new KeyValuePair<int, int>(int.Parse(subMapping.Split('=')[0]), int.Parse(subMapping.Split('=')[1]));
                    Console.WriteLine($"{pair.Key}, {pair.Value}");

                    if (!pumpNozzlesDict.ContainsKey(pair.Key))
                    {
                        pumpNozzlesDict.Add(pair.Key, new List<int> { pair.Value });
                    }
                    else
                    {
                        List<int> nozzlesForThisPump;
                        pumpNozzlesDict.TryGetValue(pair.Key, out nozzlesForThisPump);
                        if (nozzlesForThisPump != null && !nozzlesForThisPump.Contains(pair.Value))
                        {
                            nozzlesForThisPump.Add(pair.Value);
                        }
                    }
                }
            }
            else if (!string.IsNullOrEmpty(pumpNozzles) && pumpNozzles.Count(c => c == '=') == 1) // only one pump per serial port
            {
                try
                {
                    pumpNozzlesDict.Add(
                        int.Parse(pumpNozzles.Split('=')[0]),
                        new List<int> { int.Parse(pumpNozzles.Split('=')[1]) });
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
            }
            else
            {
                throw new ArgumentException("Wrong mapping between pump and its associated nozzles!");
            }

            return pumpNozzlesDict;
        }

        static Dictionary<int, List<int>> ParsePumpSiteNozzleNoList(string pumpSiteNozzleNos)
        {
            Dictionary<int, List<int>> pumpSiteNozzleNoDict = new Dictionary<int, List<int>>();

            if (!string.IsNullOrEmpty(pumpSiteNozzleNos) && pumpSiteNozzleNos.Contains(';'))
            {
                var arr = pumpSiteNozzleNos.Split(';');
                foreach (var subMapping in arr)
                {
                    var pair = new KeyValuePair<int, List<int>>(
                        int.Parse(subMapping.Split('=')[0]), subMapping.Split('=')[1].Split(',').Select(a => int.Parse(a)).ToList());

                    Console.WriteLine($"{pair.Key}, {pair.Value}");

                    if (!pumpSiteNozzleNoDict.ContainsKey(pair.Key))
                    {
                        pumpSiteNozzleNoDict.Add(pair.Key, pair.Value);
                    }
                }
            }
            else if (!string.IsNullOrEmpty(pumpSiteNozzleNos) && pumpSiteNozzleNos.Count(c => c == '=') == 1)
            {

                try
                {
                    string[] strArr = pumpSiteNozzleNos.Split('=');
                    pumpSiteNozzleNoDict.Add(
                        int.Parse(strArr[0]), new List<int> { int.Parse(strArr[1]) });
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
            }
            else
            {
                throw new ArgumentException("Wrong mapping between pump and its associated nozzles!");
            }

            return pumpSiteNozzleNoDict;
        }

        private Dictionary<int, int> InitializeNozzleLogicIdMapping(string nozzleLogicIds)
        {
            var dict = new Dictionary<int, int>();
            if (!string.IsNullOrEmpty(nozzleLogicIds))
            {
                var sequence = nozzleLogicIds.Split(';')
                    .Select(s => s.Split('='))
                    .Select(a => new { NozzleNo = int.Parse(a[0]), LogicId = int.Parse(a[1]) });

                foreach (var pair in sequence)
                {
                    if (!dict.ContainsKey(pair.NozzleNo))
                    {
                        Console.WriteLine($"nozzle, logic id: {pair.NozzleNo} - {pair.LogicId}");
                        dict.Add(pair.NozzleNo, pair.LogicId);
                    }
                }

                return dict;
            }
            else if (!string.IsNullOrEmpty(nozzleLogicIds) && nozzleLogicIds.Count(c => c == '=') == 1)
            {
                try
                {
                    string[] sequence = nozzleLogicIds.Split('=');
                    dict.Add(int.Parse(sequence[0]), int.Parse(sequence[1]));
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
                return dict;
            }
            else
            {
                throw new ArgumentException("Pump id and sub address mapping does not exist");
            }
        }

        private void InitializePumpHandlers()
        {
            var pumpIdList = GetPumpIdList(pumpIds);

            foreach (var item in pumpIdList)
            {
                var nozzleList = GetNozzleListForPump(item);
                var siteNozzleNoList = PumpSiteNozzleNoDict[item];
                HengshanPumpHandler pumpHandler = new HengshanPumpHandler(this, $"Pump_{item}", item, nozzleList, siteNozzleNoList);
                pumpHandler.OnFuelPriceChangeRequested += PumpHandler_OnFuelPriceChangeRequested;
                pumpHandlers.Add(pumpHandler);
            }
        }

        private List<int> GetNozzleListForPump(int pumpId)
        {
            List<int> nozzles;
            PumpNozzlesDict.TryGetValue(pumpId, out nozzles);
            return nozzles;
        }

        private void PumpHandler_OnFuelPriceChangeRequested(object sender, FuelPriceChangeRequestEventArgs e)
        {
            InfoLog($"Change price, Pump {e.PumpId}, Nozzle {e.NozzleId}, Price {e.Price}");

            OnFuelPriceChangeRequested?.Invoke(sender, e);
        }

        IEnumerator<IFdcPumpController> IEnumerable<IFdcPumpController>.GetEnumerator()
        {
            return pumpHandlers.GetEnumerator();
        }

        #endregion

        #region IHandler implementation

        public void Init(IContext<byte[], CommonMessage> context)
        {
            CommIdentity = context.Processor.Communicator.Identity;
            _context = context;

            this.MysqlDbContext = new MysqlDbContext();
            this.httpClientUtil = new HttpClientUtils();

            this.serverPort = CommIdentity.Replace("*:", "").ToInt();
            GetInfo();
        }

        public string CommIdentity { get; private set; }

        public async Task Process(IContext<byte[], CommonMessage> context)
        {
            
            switch(context.Incoming.Message.Handle)
            {
                //心跳，带油枪状态信息
                case 0x10:
                    {
                        //将油枪状态区分为空闲或非空闲，记录在内存。当状态有发生变化，发送到云端
                        HeartBeatMessage heartBeatMessage = (HeartBeatMessage)context.Incoming.Message;
                        SendNozzleStatus(heartBeatMessage);
                        break;
                    }
                //订单
                case 0x18:
                    {
                        //添加或修改数据库订单
                        OrderFromMachine orderFromMachine = (OrderFromMachine)context.Incoming.Message;
                        FccOrderInfo fccOrderInfo = UpLoadOrder(orderFromMachine);
                        logger.Info($"receive order from machine,database had change");
                        CreateTransaction(fccOrderInfo);
                        break;
                    }
                    /**
                //普通应答
                case 0x55:
                    {
                        CommonAnswerBack commonAnswerBack = (CommonAnswerBack)context.Incoming.Message;
                        if (commonAnswerBack.Command == 0x63) //二维码回复
                        {
                            byte[] keyBytes = { commonAnswerBack.Command, (byte)commonAnswerBack.NozzleNum };
                            var key = BitConverter.ToString(keyBytes).Replace("-", "");
                            if (_tcsDictionary.TryGetValue(key, out var value))
                            {
                                value.SetResult(commonAnswerBack);
                            }
                            else
                            {
                                logger.Info($"qrcode response:can not get tcs for dictionary");
                            }
                        }

                        break;
                    }
                // 授权回复
                case 0x65:
                    {
                        AuthorizationResponse authorizationResponse = (AuthorizationResponse)context.Incoming.Message;
                        byte[] keyBytes = { authorizationResponse.Handle, (byte)authorizationResponse.NozzleNum };
                        var key = BitConverter.ToString(keyBytes).Replace("-", "");
                        if (_tcsDictionary.TryGetValue(key, out var value))
                        {
                            value.SetResult(authorizationResponse);
                        }
                        else
                        {
                            logger.Info($"authorization response:can not get tcs for dictionary");
                        }
                        break;
                    }
                // 取消授权回复
                case 0x66:
                    {
                        UnAhorizationResponse unauthorizationResponse = (UnAhorizationResponse)context.Incoming.Message;
                        byte[] keyBytes = { unauthorizationResponse.Handle, (byte)unauthorizationResponse.NozzleNum };
                        var key = BitConverter.ToString(keyBytes).Replace("-", "");
                        if (_tcsDictionary.TryGetValue(key, out var value))
                        {
                            value.SetResult(unauthorizationResponse);
                        }
                        else
                        {
                            logger.Info($"unauthorization response:can not get tcs for dictionary");
                        }
                        break;
                    }
                    */

            }
            
            //油机的应答不用回复
            if(context.Incoming.Message.Handle != 0x55) context.Outgoing.Write(context.Incoming.Message);

        }

        private void CheckStatus(CheckCmdRequest request)
        {
            if (!statusDict.ContainsKey(request.FuelingPoint.PumpNo))
            {
                var result = statusDict.TryAdd(request.FuelingPoint.PumpNo,
                    new PumpStateHolder
                    {
                        PumpNo = request.FuelingPoint.PumpNo,
                        NozzleNo = 1,
                        State = request,
                        OperationType = LockUnlockOperation.None
                    });

                logger.Info($"Adding FuelingPoint {request.FuelingPoint.PumpNo} to dict");

                if (!result)
                {
                    statusDict.TryAdd(request.FuelingPoint.PumpNo, null);
                }
            }
            else
            {
                PumpStateHolder stateHolder = null;
                statusDict.TryGetValue(request.FuelingPoint.PumpNo, out stateHolder);

                if (stateHolder != null)
                {
                    logger.Debug($"State holder, PumpNo: {stateHolder.PumpNo}, dispenser state: {stateHolder.State.DispenserState}, " +
                        $"operation: {stateHolder.OperationType}");
                }

                if (stateHolder != null && stateHolder.OperationType != LockUnlockOperation.None)
                {
                    logger.Debug($"PumpNo: {request.FuelingPoint.PumpNo}, Last Dispenser State: {stateHolder.State.DispenserState}, " +
                        $"Current Dispenser State: {request.DispenserState}");

                    if (stateHolder.State.DispenserState == 3 && request.DispenserState == 2)
                    {
                        //Pump is locked due to lock operation
                        if (stateHolder.OperationType != LockUnlockOperation.None)
                        {
                            logger.Info("Locking done!");
                            stateHolder.State = request; //Update the state
                            OnLockUnlockCompleted?.Invoke(this, new LockUnlockEventArgs(stateHolder.OperationType, true));
                        }
                    }
                    else if (stateHolder.State.DispenserState == 2 && request.DispenserState == 3)
                    {
                        //Pump is unlocked due to unlock operation
                        if (stateHolder.OperationType != LockUnlockOperation.None)
                        {
                            logger.Info($"Unlocking done!");
                            stateHolder.State = request; //Update the state
                            OnLockUnlockCompleted?.Invoke(this, new LockUnlockEventArgs(stateHolder.OperationType, true));
                        }
                    }
                }
                else if (stateHolder != null && stateHolder.OperationType == LockUnlockOperation.None)
                {
                    if (stateHolder.State.DispenserState != request.DispenserState)
                    {
                        logger.Warn($"Observed a pump state change, {stateHolder.State.DispenserState} -> {request.DispenserState}");
                        stateHolder.State = request; //Update the state.
                    }
                }
            }
        }

        public void Write(CommonMessage cardMessage)
        {   
            _context.Outgoing.Write(cardMessage);
        }

        public async Task<CommonMessage> WriteAsync(CommonMessage request, Func<CommonMessage, CommonMessage, bool> responseCapture,
            int timeout)
        {
            var resp = await _context.Outgoing.WriteAsync(request, responseCapture, timeout);
            return resp;
        }

        #endregion

        #region IEnumerable<IFdcPumpController> implementation

        public IEnumerator<IFdcPumpController> GetEnumerator()
        {
            return pumpHandlers.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return pumpHandlers.GetEnumerator();
        }

        #endregion

        public void PendMessage(CardMessageBase message)
        {
            lock (syncObj)
            {
                queue.Enqueue(message);
            }
        }

        public bool TrySendNextMessage()
        {
            lock (syncObj)
            {
                if (queue.Count > 0)
                {
                    DebugLog($"queue count: {queue.Count}");
                    var message = commonQueue.Dequeue();
                    Write(message);
                    return true;
                }
            }

            return false;
        }

        public void StoreLatestFrameSqNo(int pumpId, byte frameSqNo)
        {
            var pump = GetPump(pumpId);
            if (pump != null)
            {
                pump.FrameSqNo = frameSqNo;
            }
        }

        public void UpdatePumpState(int pumpId, int logicId, LogicalDeviceState state)
        {
            var currentPump = GetPump(pumpId);
            currentPump?.FirePumpStateChange(state, Convert.ToByte(logicId));
        }

        public void UpdateFuelingStatus(int pumpId, FdcTransaction fuelingTransaction)
        {
            var currentPump = GetPump(pumpId);
            currentPump?.FireFuelingStatusChange(fuelingTransaction);
        }

        private HengshanPumpHandler GetPump(int pumpId)
        {
            return pumpHandlers.FirstOrDefault(p => p.PumpId == pumpId);
        }

        public void SetRealPrice(int pumpId, int price)
        {
            var currentPump = GetPump(pumpId);
            var nozzle = currentPump?.Nozzles.FirstOrDefault();
            if (nozzle != null)
                nozzle.RealPriceOnPhysicalPump = price;
        }

        #region Log methods

        private void InfoLog(string info)
        {
            logger.Info("PayTermHdlr " + info);
        }

        private void DebugLog(string debugMsg)
        {
            logger.Debug("PayTermHdlr " + debugMsg);
        }

        #endregion

        #region 二维码加油机相关方法

        /// <summary>
        /// 获取站点信息
        /// </summary>
        private void GetInfo()
        {
            Edge.Core.Domain.FccStationInfo.FccStationInfo? fccStationInfo = MysqlDbContext.FccStationInfos.FirstOrDefault();
            if(fccStationInfo != null) stationInfo = new StationInfo(fccStationInfo);
            nozzleInfoList = MysqlDbContext.NozzleInfos.ToList().Select(n => new DetailsNozzleInfoOutput(n)).ToList();
        }

        /// <summary>
        /// 接收到MQTT
        /// </summary>
        /// <param name="message"></param>
        public async void OnReceiveMqttMessage(string message)
        {
            MqttRequest? mqttRequest = JsonConvert.DeserializeObject<MqttRequest>(message);
            if (mqttRequest == null)
            {
                logger.Error($"mqtt message turn on object fail,message:{message}");
                return;
            }
            switch (mqttRequest.type)
            {
                case MQTT_TYPE.AUTHORIZATION:
                    {
                        
                        MqttAuthorizationRequest? mqttAuthorizationRequest = JsonConvert.DeserializeObject<MqttAuthorizationRequest>(mqttRequest.data);
                        await SendAuthorizationAsync(mqttAuthorizationRequest);
                        break;
                    }
                case MQTT_TYPE.UNAUTHORIZATION:
                    {
                        MqttUnAhorizationRequest? mqttUnAhorizationRequest = JsonConvert.DeserializeObject<MqttUnAhorizationRequest>(mqttRequest.data);
                        await SendUnAuthorizartion(mqttUnAhorizationRequest);
                        break;
                    }
                case MQTT_TYPE.PAID:
                    {
                        MqttPaidRequest? mqttPaidRequest = JsonConvert.DeserializeObject<MqttPaidRequest>(mqttRequest.data);
                        await SendActuallyPaid(mqttPaidRequest);
                        break;
                    }
                case MQTT_TYPE.REFUND:
                    {
                        MqttRefundRequest? mqttRefundRequest = JsonConvert.DeserializeObject<MqttRefundRequest>(mqttRequest.data);
                        await OnRecieveOrderRefund(mqttRefundRequest);
                        break;
                    }
            }
        }

        /// <summary>
        /// 发送二维码信息给油机
        /// </summary>
        /// <param name="tcpClient"></param>
        public async void SendQRCodeAsync()
        {
            string smallProgram = stationInfo?.SmallProgram ?? "";
            if (string.IsNullOrEmpty(smallProgram))
            {
                logger.Info($"can not get smallProgram link");
                return;
            }

            List<DetailsNozzleInfoOutput> nozzles = nozzleInfoList.FindAll(nozzle => nozzle.Port == serverPort);
            foreach (var item in nozzles)
            {
                //List<Byte> list = new List<Byte>();
                //byte[] commandAndNozzle = { 0x63, (byte)item.NozzleNum };
                //string qrCode = smallProgram + "/" + item.NozzleNum;
                //byte[] qrCodeBytes = Encoding.ASCII.GetBytes(qrCode);
                //list.AddRange(commandAndNozzle);
                //list.Add((byte)qrCodeBytes.Length);
                //list.AddRange(qrCodeBytes);
                //byte[] sendBytes = content2data(list.ToArray(), null);

                SendQrCode sendQrCode = new SendQrCode(item.NozzleNum, smallProgram, getFrame(null));
                byte[] commandAndNozzle = { sendQrCode.Handle, (byte)sendQrCode.NozzleNum };

                Thread.Sleep(5000);
                CommonMessage commonMessage = await SendMessageToMaichine($"发送{sendQrCode.NozzleNum}号枪二维码",(request,response) => 
                {
                    if(response.Handle == (byte)CommonMessage.Command.COMMON)
                    {
                        CommonAnswerBack commonAnswerBack = (CommonAnswerBack)response;
                        return commonAnswerBack.Command == (byte)CommonMessage.Command.SEND_QR_CODE && commonAnswerBack.NozzleNum == sendQrCode.NozzleNum;
                    }
                    return false;
                }, sendQrCode);
                //CommonMessage commonMessage = await SendMessageToMaichine("发送二维码", BitConverter.ToString(commandAndNozzle).Replace("-", ""), sendQrCode);
                //CommonMessage commonMessage = await SendRequestToMachine("发送二维码", BitConverter.ToString(commandAndNozzle).Replace("-", ""), sendBytes);
                if (commonMessage.IsError && commonMessage.TheErrorType == CommonMessage.ErrorType.DISCONNECT) break;
            }

            //var testAuthorization = new MqttAuthorizationRequest()
            //{
            //    NozzleNum = 1,
            //    AuthorizationTime = DateTime.Now,
            //    AuthorizationType = 1,
            //    Value = 3.00m
            //};
            //await SendAuthorization(testAuthorization);

            //var testUnAuthorization = new MqttUnAhorizationRequest()
            //{
            //    NozzleNum = 1,
            //    AuthorizationTime = DateTime.Now,
            //    Ttc = 111
            //};
            //await SendUnAuthorizartion(testUnAuthorization);
        }

        /// <summary>
        /// 发送实付金额给油机
        /// </summary>
        /// <param name="orderInfo"></param>
        public async Task SendActuallyPaid(MqttPaidRequest? request) 
        {
            if (request == null)
            {
                logger.Error($"mqtt get paid request is null");
                return;
            }

            //通知云端当前已收到消息
            OnGetPaidInfo onGetPaidInfo = new OnGetPaidInfo()
            {
                Id = request.Id,
                Result = 1
            };
            await httpClientUtil.SendRecievePaidNotice(JsonConvert.SerializeObject(onGetPaidInfo));

            FccOrderInfo? fccOrderInfo = MysqlDbContext.FccOrderInfos.FirstOrDefault(order => order.CloundOrderId == request.Id);
            if (fccOrderInfo == null)
            {
                logger.Error($"[mqtt paid order notice]:can not find order by clounid:{request.Id}");
                return;
            }
            fccOrderInfo.AmountPayable = request.ActualPaymentAmount;
            fccOrderInfo.PaymentTime = request.TransactionTime;
            fccOrderInfo.PaymentStatus = 1;
            MysqlDbContext.SaveChanges();

            //SendActuallyPaid sendActuallyPaid = new SendActuallyPaid(orderInfo.NozzleNum, orderInfo.Ttc, orderInfo.AmountPayable ?? orderInfo.Amount, getFrame(null));
            //byte[] commandAndNozzle = { sendActuallyPaid.Handle, (byte)sendActuallyPaid.NozzleNum };
            //await SendMessageToMaichine("发送实付金额", (request, response) =>
            //{
            //    if (response.Handle == (byte)CommonMessage.Command.SEND_NEED_AMOUNT)
            //    {
            //        CommonAnswerBack commonAnswerBack = (CommonAnswerBack)response;
            //        return commonAnswerBack.Command == (byte)CommonMessage.Command.SEND_NEED_AMOUNT && commonAnswerBack.NozzleNum == sendActuallyPaid.NozzleNum;
            //    }
            //    return false;
            //}, sendActuallyPaid);
            //await SendMessageToMaichine("发送实付金额", BitConverter.ToString(commandAndNozzle).Replace("-", ""), sendActuallyPaid);
        }

        /// <summary>
        /// 发送授权请求给油机
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public async Task SendAuthorizationAsync(MqttAuthorizationRequest? request)
        {
            if(request == null)
            {
                logger.Error($"mqtt authorization request is null");
                return;
            }

            //添加订单到数据库
            DateTime authorizationTime = request.AuthorizationTime ?? DateTime.Now;
            FccOrderInfo fccOrderInfo = request.ToComponent(authorizationTime);
            MysqlDbContext.FccOrderInfos.Add(fccOrderInfo);

            //发送授权申请到油机
            SendAuthorization sendAuthorization = new SendAuthorization((int)request.NozzleId, authorizationTime, 1,request.OriginalAmount, getFrame(null));
            byte[] commandAndNozzle = { sendAuthorization.Handle, (byte)sendAuthorization.NozzleNum };
            CommonMessage commonMessage = await SendMessageToMaichine("发送授权请求", (request, response) =>
            {
                if (response.Handle == (byte)CommonMessage.Command.ACCREDIT)
                {
                    AuthorizationResponse authorization = (AuthorizationResponse)response;
                    return authorization.NozzleNum == sendAuthorization.NozzleNum;
                }
                return false;
            }, sendAuthorization);

            //发送授权结果给云端
            string authorizationResultJson = string.Empty;
            SendAuthorizationResult sendAuthorizationResult = new SendAuthorizationResult();
            sendAuthorizationResult.NozzleId = request.NozzleId;

            if (commonMessage.IsError)
            {
                ErrorMessage errorMessage = (ErrorMessage)commonMessage;
                switch (errorMessage.TheErrorType)
                {
                    case CommonMessage.ErrorType.DISCONNECT:
                        sendAuthorizationResult.OilMachineStatus = OilMachineStatus.Disconnected;
                        break;
                    case CommonMessage.ErrorType.TIMEOUT:
                        sendAuthorizationResult.OilMachineStatus = OilMachineStatus.AuthorizationTimeout;
                        break;
                }
            }
            else
            {
                AuthorizationResponse authorization = (AuthorizationResponse)commonMessage;
                if (authorization.Result == 0)
                {
                    sendAuthorizationResult.OilMachineStatus = OilMachineStatus.Failed;
                }
                else
                {
                    sendAuthorizationResult.OilMachineStatus = OilMachineStatus.Success;
                    sendAuthorizationResult.TransactionNumber = authorization.Ttc.ToString();
                    fccOrderInfo.Ttc = authorization.Result;
                }
            }
            HttpResponseMessage httpResponseMessage = await httpClientUtil.SendAuthorizationResult(JsonConvert.SerializeObject(sendAuthorizationResult));
            logger.Info($"send authorization result response:{JsonConvert.SerializeObject(httpResponseMessage.Content)}");

            //更新订单
            MysqlDbContext.SaveChanges();
        }

        /// <summary>
        /// 发送取消授权请求给油机
        /// </summary>
        /// <param name="request"></param>
        public async Task SendUnAuthorizartion(MqttUnAhorizationRequest? request)
        {
            if (request == null)
            {
                logger.Error($"mqtt unauthorization request is null");
                return;
            }

            //从请求信息中获取流水号与授权时间，没有就到数据库查找
            int ttc = 0;
            DateTime authorizationTime = request.AuthorizationTime ?? DateTime.Now;
            bool ttsIntResult = int.TryParse(request.TransactionNumber, out ttc);
            if (request.AuthorizationTime == null || !ttsIntResult)
            {
                FccOrderInfo? fccOrderInfo = MysqlDbContext.FccOrderInfos.FirstOrDefault(order => order.CloundOrderId == request.Id);
                if(fccOrderInfo != null)
                {
                    ttc = fccOrderInfo.Ttc;
                    authorizationTime = fccOrderInfo.AuthorizationTime;
                }
            }

            SendUnAuthorizationResult sendUnAuthorizationResult = new SendUnAuthorizationResult();
            sendUnAuthorizationResult.NozzleId = request.NozzleId;
            sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.Success;

            if (ttc != 0)
            {
                SendUnAuthorization sendUnAuthorization = new SendUnAuthorization((int)request.NozzleId, authorizationTime, ttc, getFrame(null));
                byte[] commandAndNozzle = { sendUnAuthorization.Handle, (byte)sendUnAuthorization.NozzleNum };

                CommonMessage commonMessage = await SendMessageToMaichine("发送取消授权请求", (request, response) =>
                {
                    if (response.Handle == (byte)CommonMessage.Command.CANCEL_ACCREDIT)
                    {
                        UnAhorizationResponse unauthorization = (UnAhorizationResponse)response;
                        return unauthorization.NozzleNum == sendUnAuthorization.NozzleNum;
                    }
                    return false;
                }, sendUnAuthorization);
                if (commonMessage.IsError)
                {
                    ErrorMessage errorMessage = (ErrorMessage)commonMessage;
                    switch (errorMessage.TheErrorType)
                    {
                        case CommonMessage.ErrorType.DISCONNECT:
                            sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.Disconnected;
                            break;
                        case CommonMessage.ErrorType.TIMEOUT:
                            sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.AuthorizationTimeout;
                            break;
                    }
                }
                else
                {
                    UnAhorizationResponse unAuthorization = (UnAhorizationResponse)commonMessage;
                    if (unAuthorization.Result == 0)
                    {
                        sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.Failed;
                    }
                    else
                    {
                        sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.Success;
                    }
                }
            }
            else
            {
                sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.TransactionNumberNotFound;
            }

            HttpResponseMessage httpResponseMessage = await httpClientUtil.SendUnAuthorizationResult(JsonConvert.SerializeObject(sendUnAuthorizationResult));
            logger.Info($"send Unauthorization result response:{JsonConvert.SerializeObject(httpResponseMessage.Content)}");
        }

        /// <summary>
        /// 接收到云端发送订单退款信息
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        private async Task OnRecieveOrderRefund(MqttRefundRequest? request)
        {
            if (request == null)
            {
                logger.Error($"mqtt OnRecieveOrderRefund request is null");
                return;
            }
            //通知云端当前已收到消息
            OnGetRefundInfo onGetRefundInfo = new OnGetRefundInfo()
            {
                Id = request.Id,
                Result = 1
            };
            await httpClientUtil.SendRecieveRefundNotice(JsonConvert.SerializeObject(onGetRefundInfo));

            FccOrderInfo? fccOrderInfo = MysqlDbContext.FccOrderInfos.FirstOrDefault(order => order.CloundOrderId == request.Id);
            if (fccOrderInfo == null)
            {
                logger.Error($"[mqtt refund order notice]:can not find order by clounid:{request.Id}");
                return;
            }
            fccOrderInfo.AmountPayable = request.ActualPaymentAmount;
            fccOrderInfo.PaymentStatus = 2;
            MysqlDbContext.SaveChanges();

        }

        //public void SetTcpClient(TcpClient? tcpClient, int? serverPort)
        //{
        //    this.client = tcpClient;
        //    this.serverPort = serverPort;
        //    checkDisConnectTask = new TaskCompletionSource<ErrorMessage>();
        //}

        //public void OnTcpDisconnect()
        //{
        //    this.client = null;
        //    ErrorMessage errorMessage = new ErrorMessage()
        //    {
        //        IsError = true,
        //        TheErrorType = CommonMessage.ErrorType.DISCONNECT,
        //        ErrorMessage = $"the client is disconnet"
        //    };
        //    checkDisConnectTask.SetResult(errorMessage);
        //}

        /// <summary>
        /// 发送消息到油机，3秒的超时，重试三次
        /// </summary>
        /// <param name="sendTag">发送的消息类型，用于日志记录</param>
        /// <param name="sendKey">发送的消息key,用于存储 TaskCompletionSource</param>
        /// <param name="requestBytes">实际发送消息</param>
        /// <returns></returns>
        private async Task<CommonMessage> SendMessageToMaichine(string sendTag, Func<CommonMessage, CommonMessage, bool> responseCapture,CommonMessage sendMessage)
        {
            int retryCount = 0;
            while (retryCount < 3)
            {
                try
                {
                    var response =  await this.Context.Outgoing.WriteAsyncAndCheckIsConnect(sendMessage, responseCapture, 3000);


                    //超时重试
                    if (response.ResponseType == WriteResponseType.TIME_OUT || response.Data == null)
                    {
                        retryCount++;
                        logger.Info($"{sendTag}: time out,retrying... ({retryCount} / 3)");
                        continue;
                    }

                    //链接断开不再发送
                    if (response.ResponseType == WriteResponseType.DISCONNECT)
                    {
                        var isConnect = (bool)(response.Data ?? false);
                        if (!isConnect) return new ErrorMessage()
                        {
                            IsError = true,
                            TheErrorType = CommonMessage.ErrorType.DISCONNECT,
                            ErrorMessage = $"the client is disconnet"
                        };

                    }
                    Console.WriteLine("");
                    //返回信息
                    return (CommonMessage)response.Data;
                }
                catch (Exception)
                {
                    retryCount++;
                    logger.Info($"{sendTag}: error,retrying... ({retryCount} / 3)");
                }
                finally
                {
                    if (retryCount >= 3)
                    {
                        logger.Info($"{sendTag}: is time out add retry 3 time");
                    }
                }
            }
            return new ErrorMessage()
            {
                IsError = true,
                TheErrorType = CommonMessage.ErrorType.TIMEOUT,
                ErrorMessage = $"{sendTag}: can not receive response after 3 retries"
            };
        }

        ///// <summary>
        ///// 发送消息到油机，3秒的超时，重试三次
        ///// </summary>
        ///// <param name="sendTag">发送的消息类型，用于日志记录</param>
        ///// <param name="sendKey">发送的消息key,用于存储 TaskCompletionSource</param>
        ///// <param name="requestBytes">实际发送消息</param>
        ///// <returns></returns>
        //private async Task<CommonMessage> SendMessageToMaichine(string sendTag, string sendKey,CommonMessage sendMessage)
        //{
        //    int retryCount = 0;
        //    while (retryCount < 3)
        //    {
        //        try
        //        {
        //            var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
        //            bool isAdd = _tcsDictionary.TryAdd(sendKey, new TaskCompletionSource<CommonMessage>());
        //            logger.Info($"{sendTag}: add request {sendKey} to dic is {isAdd}");

        //            Write(sendMessage);

        //            TaskCompletionSource<CommonMessage>? value;
        //            TaskCompletionSource<CommonMessage> tcs;
        //            if (_tcsDictionary.TryGetValue(sendKey, out value))
        //            {
        //                tcs = value;
        //            }
        //            else
        //            {
        //                tcs = new TaskCompletionSource<CommonMessage>();
        //            }
        //            Task checkOutTime = Task.Delay(Timeout.Infinite, cts.Token);
        //            var response = await Task.WhenAny(tcs.Task, checkOutTime, checkDisConnectTask.Task);
        //            //超时重试
        //            if (response == checkOutTime)
        //            {
        //                retryCount++;
        //                logger.Info($"{sendTag}-{sendKey}: time out,retrying... ({retryCount} / 3)");
        //                continue;
        //            }
        //            //CommonMessage response = await tcs.Task.WaitAsync(cts.Token);
        //            _tcsDictionary.TryRemove(sendKey, out _);

        //            //链接断开不再发送
        //            if(response == checkDisConnectTask.Task)
        //            {
        //                return new ErrorMessage()
        //                {
        //                    IsError = true,
        //                    TheErrorType = CommonMessage.ErrorType.DISCONNECT,
        //                    ErrorMessage = $"the client is disconnet"
        //                };
        //            }

        //            //返回信息
        //            return await (Task<CommonMessage>)response;
        //        }
        //        catch (Exception)
        //        {
        //            retryCount++;
        //            logger.Info($"{sendTag}-{sendKey}: error,retrying... ({retryCount} / 3)");
        //        }
        //        finally
        //        {
        //            if (retryCount >= 3)
        //            {
        //                logger.Info($"{sendTag}-{sendKey}: is time out add retry 3 time");
        //                _tcsDictionary.TryRemove(sendKey, out _);
        //            }
        //        }
        //    }
        //    return new ErrorMessage()
        //    {
        //        IsError = true,
        //        TheErrorType = CommonMessage.ErrorType.TIMEOUT,
        //        ErrorMessage = $"{sendTag}: can not receive response after 3 retries"
        //    };
        //}

        ///// <summary>
        ///// 发送消息到油机，3秒的超时，重试三次
        ///// </summary>
        ///// <param name="sendTag">发送的消息类型，用于日志记录</param>
        ///// <param name="sendKey">发送的消息key,用于存储 TaskCompletionSource</param>
        ///// <param name="requestBytes">实际发送消息</param>
        ///// <returns></returns>
        ///// <exception cref="TimeoutException"></exception>
        //private async Task<CommonMessage> SendRequestToMachine(string sendTag,string sendKey, byte[] requestBytes)
        //{
        //    int retryCount = 0;
        //    while(retryCount < 3)
        //    {
        //        try
        //        {
        //            var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
        //            bool isAdd = _tcsDictionary.TryAdd(sendKey, new TaskCompletionSource<CommonMessage>());
        //            logger.Info($"{sendTag}: add request {sendKey} to dic is {isAdd}");

        //            if (client != null)
        //            {
        //                client?.Client?.Send(requestBytes);
        //            } else
        //            {
        //                return new ErrorMessage()
        //                {
        //                    IsError = true,
        //                    TheErrorType = CommonMessage.ErrorType.DISCONNECT,
        //                    ErrorMessage = $"the client is disconnet"
        //                };
        //            }
                    
        //            logger.Info($"send request to machine:{BitConverter.ToString(requestBytes).Replace("-", " ")}");

        //            TaskCompletionSource<CommonMessage>? value;
        //            TaskCompletionSource<CommonMessage> tcs;
        //            if(_tcsDictionary.TryGetValue(sendKey, out value))
        //            {
        //                tcs = value;
        //            } else
        //            {
        //                tcs = new TaskCompletionSource<CommonMessage>();
        //            }
        //            Task checkOutTime = Task.Delay(Timeout.Infinite, cts.Token);
        //            var response = await Task.WhenAny(tcs.Task, checkOutTime, checkDisConnectTask.Task);
        //            if(response == checkOutTime)
        //            {
        //                retryCount++;
        //                logger.Info($"{sendTag}-{sendKey}: time out,retrying... ({retryCount} / 3)");
        //                continue;
        //            }
        //            //CommonMessage response = await tcs.Task.WaitAsync(cts.Token);
        //            _tcsDictionary.TryRemove(sendKey, out _);
        //            if (response == checkDisConnectTask.Task)
        //            {
        //                return new ErrorMessage()
        //                {
        //                    IsError = true,
        //                    TheErrorType = CommonMessage.ErrorType.DISCONNECT,
        //                    ErrorMessage = $"the client is disconnet"
        //                };
        //            }
        //            return await (Task<CommonMessage>)response;
        //        } catch (Exception)
        //        {
        //            retryCount++;
        //            logger.Info($"{sendTag}-{sendKey}: error,retrying... ({retryCount} / 3)");
        //        } finally
        //        {
        //            if(retryCount >= 3)
        //            {
        //                logger.Info($"{sendTag}-{sendKey}: is time out add retry 3 time");
        //                _tcsDictionary.TryRemove(sendKey,out _);
        //            }
        //        }
        //    }
        //    return new ErrorMessage()
        //    {
        //        IsError = true,
        //        TheErrorType = CommonMessage.ErrorType.TIMEOUT,
        //        ErrorMessage = $"{sendTag}: can not receive response after 3 retries"
        //    };
        //}

        /// <summary>
        /// 添加或修改订单
        /// </summary>
        /// <param name="order">接收到油机的订单信息</param>
        /// <returns></returns>
        public FccOrderInfo UpLoadOrder(OrderFromMachine order)
        {
            //接收到油机发送过来的订单信息
            OrderFromMachine orderFromMachine = (OrderFromMachine)order;
            string? oilName = MysqlDbContext.OilInfos.Where(oil => orderFromMachine.oilCode.Equals(oil.Code)).Select(oil => oil.Name).FirstOrDefault();
            FccOrderInfo orderByMessage = orderFromMachine.ToComponent(oilName);

            /** 根据枪号+流水号+授权时间来确定订单，因为冷启动后流水号会从头开始计算
             * 后支付时直接将数据库直接插入
             * 预支付时由于是云端先创建订单，发起授权响应成功后会插入数据库，响应成功时会回复授权时间，枪号，流水号
             */
            FccOrderInfo? fccOrderInfo = MysqlDbContext.FccOrderInfos
                .Where(order =>
                    order.NozzleNum == orderFromMachine.nozzleNum && order.Ttc == orderFromMachine.ttc
                    && order.AuthorizationTime == orderFromMachine.dispenserTime)
                .FirstOrDefault();
            if (fccOrderInfo == null)
            {
                logger.Info($"receive order from machine,find order from database is null");
                MysqlDbContext.FccOrderInfos.Add(orderByMessage);
                MysqlDbContext.SaveChanges();
                return orderByMessage;
            }
            else
            {
                logger.Info($"receive order from machine,padding data right now");
                orderFromMachine.PaddingAuthorizationOrderData(fccOrderInfo);
                MysqlDbContext.SaveChanges();
                return fccOrderInfo;
            }
            
        }

        private async void CreateTransaction(FccOrderInfo fccOrderInfo)
        {
            CreateTransaction createTransaction = new CreateTransaction(fccOrderInfo);
            logger.Info($"create transaction, {JsonConvert.SerializeObject(createTransaction)}");
            HttpResponseMessage httpResponseMessage = await httpClientUtil.CreateTransaction(JsonConvert.SerializeObject(createTransaction));

            //var b = httpResponseMessage.Content;
            //var a = httpResponseMessage;
            string responseStr = await httpResponseMessage.Content.ReadAsStringAsync();
            Response<CreateTransactionResponse>? response = JsonConvert.DeserializeObject<Response<CreateTransactionResponse>>(responseStr);
            logger.Info($"reveice create transaction response:{JsonConvert.SerializeObject(response)}");
            
            fccOrderInfo.CloundOrderId = response?.data?.Id;
            fccOrderInfo.UploadState = response?.data == null ? 0 : 1;
            MysqlDbContext.SaveChanges();
        }

        /// <summary>
        /// 发送油枪状态给云端
        /// </summary>
        /// <param name="nozzleState"></param>
        private async void SendNozzleStatus(HeartBeatMessage heartBeatMessage)
        {
            //提取出状态有变化的油枪，打包成要发送至云端的数据，添加到列表
            List<SendNozzleStatu> sendNozzleStatus = new List<SendNozzleStatu>();
            foreach (var nozzleState in heartBeatMessage.NozzleStatus)
            {
                if (nozzleStatusDic.TryGetValue(nozzleState.NozzleNum, out var value))
                {
                    if (nozzleState.STATU == value) continue;
                }
                //保存变量
                nozzleStatusDic[nozzleState.NozzleNum] = nozzleState.STATU;

                //查找fcc数据库油枪id
                DetailsNozzleInfoOutput? detailsNozzleInfoOutput = nozzleInfoList.Find(nozzle => nozzle.NozzleNum == nozzleState.NozzleNum);
                if (detailsNozzleInfoOutput == null)
                {
                    logger.Error($"can not find nozzleInfo from nozzleInfoList:{nozzleState.NozzleNum} ,send nozzle state fail");
                    continue;
                }
                SendNozzleStatu sendNozzleStatu = new SendNozzleStatu(detailsNozzleInfoOutput.Id, nozzleState);
                sendNozzleStatus.Add(sendNozzleStatu);
            }

            if (sendNozzleStatus.IsNullOrEmpty()) return;

            //发送云端
            string reuqestJson = JsonConvert.SerializeObject(sendNozzleStatus);
            logger.Info($"send nozzle state to cloud,{reuqestJson}");
            try
            {
                HttpResponseMessage httpResponseMessage = await httpClientUtil.SendNozzleStatu(reuqestJson);
                Response<object>? response = JsonConvert.DeserializeObject<Response<object>>(await httpResponseMessage.Content.ReadAsStringAsync());
                logger.Info($"reveice send nozzle state response:{JsonConvert.SerializeObject(response)}");
            } catch (Exception ex)
            {
                logger.Error($"send nozzle stat fail:{ex.Message}");
            }
            
        }

        /// <summary>
        /// 获取发送帧号
        /// </summary>
        /// <param name="sendFrame"></param>
        /// <returns></returns>
        private byte getFrame(byte? sendFrame)
        {
            byte frameNo = 0x00;
            if (sendFrame == null)
            {
                lock (lockFrame)
                {
                    if (frame == 0x3f)
                    {
                        frameNo = 0x00;
                    }
                    else
                    {
                        frameNo = (byte)(frame++);
                    }
                }
            }
            else
            {
                frameNo = sendFrame.Value;
            }
            return frameNo;
        }

        ///// <summary>
        ///// 传入有效数据，拼接为要发送给油机包
        ///// </summary>
        ///// <param name="content"></param>
        ///// <returns></returns>
        //public byte[] content2data(byte[] content, byte? sendFrame)
        //{
        //    List<byte> list = new List<byte>();
        //    //目标地址，源地址，帧号
        //    byte frameNo = 0x00;
        //    if (sendFrame == null)
        //    {
        //        lock (lockFrame)
        //        {
        //            if (frame == 0x3f)
        //            {
        //                frameNo = 0x00;
        //            }
        //            else
        //            {
        //                frameNo = (byte)(frame++);
        //            }
        //        }
        //    }
        //    else
        //    {
        //        frameNo = sendFrame.Value;
        //    }

        //    byte[] head = new byte[] { 0xFF, 0xE0, frameNo };
        //    byte[] length = Int2BCD(content.Length);
        //    list.AddRange(head);
        //    list.AddRange(length);
        //    list.AddRange(content);
        //    byte[] crc = HengshanCRC16.ComputeChecksumToBytes(list.ToArray());
        //    list.AddRange(crc);
        //    List<byte> addFAList = addFA(list);
        //    addFAList.Insert(0, 0xFA);
        //    return addFAList.ToArray();
        //}

        //public int Bcd2Int(byte byte1, byte byte2)
        //{
        //    // 提取第一个字节的高四位和低四位  
        //    int digit1 = (byte1 >> 4) & 0x0F; // 高四位  
        //    int digit2 = byte1 & 0x0F;        // 低四位  

        //    // 提取第二个字节的高四位和低四位  
        //    int digit3 = (byte2 >> 4) & 0x0F; // 高四位  
        //    int digit4 = byte2 & 0x0F;        // 低四位  

        //    // 组合成一个整数  
        //    int result = digit1 * 1000 + digit2 * 100 + digit3 * 10 + digit4;

        //    return result;
        //}

        //public byte[] Int2BCD(int number)
        //{
        //    // 提取千位、百位、十位和个位  
        //    int thousands = number / 1000;
        //    int hundreds = (number / 100) % 10;
        //    int tens = (number / 10) % 10;
        //    int units = number % 10;

        //    // 将千位和百位组合成一个字节（千位在高四位，百位在低四位）  
        //    byte firstByte = (byte)((thousands * 16) + hundreds); // 乘以16相当于左移4位  

        //    // 将十位和个位组合成一个字节（十位在高四位，个位在低四位）  
        //    byte secondByte = (byte)((tens * 16) + units);

        //    // 返回结果数组  
        //    return new byte[] { firstByte, secondByte };
        //}

        //public List<Byte> addFA(List<Byte> list)
        //{
        //    List<byte> result = new List<byte>();

        //    foreach (byte b in list)
        //    {
        //        if (b == 0xFA)
        //        {
        //            result.Add(0xFA);
        //            result.Add(0xFA);
        //        }
        //        else
        //        {
        //            result.Add(b);
        //        }
        //    }

        //    return result;
        //}

        ///// <summary>
        ///// 将数值转为byte[]
        ///// </summary>
        ///// <param name="value">数值</param>
        ///// <param name="length">数组长度，不够高位补0</param>
        ///// <returns></returns>
        ///// <exception cref="ArgumentException"></exception>
        //public static byte[] NumberToByteArrayWithPadding(int value, int length)
        //{
        //    if (length < 0)
        //    {
        //        throw new ArgumentException("Length must be non-negative.");
        //    }

        //    // 创建一个指定长度的字节数组
        //    byte[] paddedBytes = new byte[length];

        //    // 确保是大端序
        //    for (int i = 0; i < length && i < 4; i++)
        //    {
        //        paddedBytes[length - 1 - i] = (byte)(value >> (i * 8));
        //    }

        //    return paddedBytes;
        //}

        //public static byte[] FormatDecimal(decimal value)
        //{
        //    // 四舍五入到两位小数
        //    decimal roundedValue = Math.Round(value, 2, MidpointRounding.AwayFromZero);
        //    int valueInt = (int)(roundedValue * 100m);
        //    return NumberToByteArrayWithPadding(valueInt, 3); ;
        //}

        ///// <summary>
        ///// 将时间转为 BCD 
        ///// </summary>
        ///// <param name="dateTime"></param>
        ///// <returns></returns>
        //public static byte[] ConvertDateTimeToByteArray(DateTime dateTime)
        //{
        //    // 创建byte数组
        //    byte[] result = new byte[7];

        //    // 年份处理
        //    int year = dateTime.Year;
        //    result[0] = (byte)((year / 1000) * 16 + (year / 100) % 10); // 千年和百年
        //    result[1] = (byte)((year / 10) % 10 * 16 + year % 10);      // 十年和个年

        //    // 月、日、小时、分钟、秒直接转换为BCD
        //    result[2] = (byte)(dateTime.Month / 10 * 16 + dateTime.Month % 10);
        //    result[3] = (byte)(dateTime.Day / 10 * 16 + dateTime.Day % 10);
        //    result[4] = (byte)(dateTime.Hour / 10 * 16 + dateTime.Hour % 10);
        //    result[5] = (byte)(dateTime.Minute / 10 * 16 + dateTime.Minute % 10);
        //    result[6] = (byte)(dateTime.Second / 10 * 16 + dateTime.Second % 10);

        //    return result;
        //}

        // CRC16 constants  
        const ushort CRC_ORDER16 = 16;
        const ushort CRC_POLYNOM16 = 0x1021;
        const ushort CRC_CRCINIT16 = 0xFFFF;
        const ushort CRC_CRCXOR16 = 0x0000;
        const ushort CRC_MASK = 0xFFFF;
        const ushort CRC_HIGHEST_BIT = (ushort)(1 << (CRC_ORDER16 - 1));
        const ushort TGT_CRC_DEFAULT_INIT = 0xFFFF;
        public static ushort Crc16(byte[] buffer, ushort length)
        {
            ushort crc_rc = TGT_CRC_DEFAULT_INIT;

            for (int i = 0; i < length; i++)
            {
                byte c = buffer[i];
                for (ushort j = 0x80; j != 0; j >>= 1)
                {
                    ushort crc_bit = (ushort)((crc_rc & CRC_HIGHEST_BIT) != 0 ? 1 : 0);
                    crc_rc <<= 1;

                    if ((c & j) != 0)
                    {
                        crc_bit = (ushort)((crc_bit == 0) ? 1 : 0);
                    }

                    if (crc_bit != 0)
                    {
                        crc_rc ^= CRC_POLYNOM16;
                    }
                }
            }

            return (ushort)((crc_rc ^ CRC_CRCXOR16) & CRC_MASK);
        }
        #endregion
    }

    public class HengshanPayTerminalHanlderGroupConfigV1
    {
        public string PumpIds { get; set; }

        public List<PumpSubAddress> PumpSubAddresses { get; set; }
    }

    public class HengshanPayTerminalHanlderGroupConfigV2
    {
        public string PumpIds { get; set; }

        public List<PumpSubAddress> PumpSubAddresses { get; set; }

        public List<PumpNozzleLogicId> PumpNozzleLogicIds { get; set; }

        public List<PumpSiteNozzleNo> PumpSiteNozzleNos { get; set; }

        public List<NozzleLogicId> NozzleLogicIds { get; set; }
    }

    public class PumpSubAddress
    {
        public byte PumpId { get; set; }

        public byte SubAddress { get; set; }
    }

    public class PumpNozzleLogicId
    {
        public byte PumpId { get; set; }

        public string LogicIds { get; set; }
    }

    public class PumpSiteNozzleNo
    {
        public byte PumpId { get; set; }

        public string SiteNozzleNos { get; set; }
    }

    public class NozzleLogicId
    {
        public byte NozzleNo { get; set; }

        public byte LogicId { get; set; }
    }
}
