using Edge.Core.Processor;
using Edge.Core.IndustryStandardInterface.Pump;
using Dfs.WayneChina.CardTrxMonitor.Models;
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using Wayne.FDCPOSLibrary;
using System.Threading.Tasks;
using System.Threading;
using Dfs.WayneChina.CardTrxManager;
using Dfs.WayneChina.CardTrxManager.TrxSubmitter;

namespace Dfs.WayneChina.CardTrxMonitor
{
    /// <summary>
    /// Entity that pulls a finished IC card transaction from MySQL and publishes it to FdcServer.
    /// </summary>
    public class CardTrxMonitorApp : IAppProcessor, IFdcPumpController
    {
        #region Properties

        public string Name => "CardTrxMonitor";

        public int PumpId { get; private set; }
        public byte NozzleId { get; private set; }

        //This is a fake pump.
        public int PumpPhysicalId => 0;

        private List<LogicalNozzle> nozzles = new List<LogicalNozzle>();
        public IEnumerable<LogicalNozzle> Nozzles => nozzles;

        //China domestic standard
        public int AmountDecimalDigits => 2;
        public int VolumeDecimalDigits => 2;
        public int PriceDecimalDigits => 2;
        public int VolumeTotalizerDecimalDigits => 2;

        public string MetaConfigName { get; set; }

        #endregion

        #region Fields

        private System.Timers.Timer _timer;
        private int scanInterval;
        private int siteNozzleNo;
        private int bindingBarcode;
        private CloudCredential cloudCredential;
        private TrxSubmitter submitter;
        #endregion

        #region Logger

        NLog.Logger logger = NLog.LogManager.LoadConfiguration("NLog.config").GetLogger("CardTrxMonitor");

        #endregion

        #region Constructor

        public CardTrxMonitorApp(int pumpId, int nozzleId, int siteNozzleNo, int barcode, int scanInterval,
            string username, string password, string authServiceBaseUrl, string transactionServiceBaseUrl, string deviceSN)
        {
            PumpId = pumpId;
            NozzleId = Convert.ToByte(nozzleId);
            this.siteNozzleNo = siteNozzleNo;
            bindingBarcode = barcode;
            nozzles.Add(new LogicalNozzle(pumpId, 1, NozzleId, null));
            this.scanInterval = scanInterval;

            cloudCredential = new CloudCredential
            {
                UserName = username,
                Password = password,
                AuthServiceBaseUrl = authServiceBaseUrl,
                TransactionServiceBaseUrl = transactionServiceBaseUrl,
                DeviceSN = deviceSN
            };

            submitter = new TrxSubmitter(PumpId, cloudCredential);
        }

        #endregion

        #region IApplication implementation


        public void Init(IEnumerable<IProcessor> processors)
        {

        }

        public Task<bool> Start()
        {
            _timer = new System.Timers.Timer();
            _timer.Interval = scanInterval * 1000;
            _timer.Elapsed += TimeElapsed;
            _timer.Start();

            return Task.FromResult(true);
        }

        private void TimeElapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            Log("Timer reset...");

            try
            {
                FindTransaction();
            }
            catch (Exception ex)
            {
                Log($"{ex}");
            }
        }

        private void Log(string message)
        {
            logger.Info($"Monitor{PumpId}, {message}");
        }

        private void FindTransaction()
        {
            Log("start to check db...");

            try
            {
                using (var _context = new SpsDbContext())
                {
                    var cardTrx = _context.TCardtrx.FirstOrDefault(t => t.PumpNo == PumpId);

                    if (cardTrx != null && IsTrdTypeAllowed(cardTrx.TrdType) && cardTrx.Mon != 0)
                    {
                        //Before processing is done against the found transaction, stop the timer in case that
                        //it takes a while and the timer is triggerred for a second time and exception raised.
                        _timer.Stop();

                        if (cardTrx.TrdType == 6)
                        {
                            Log("Non-card fuelling");
                        }
                        else if (cardTrx.TrdType == 1)
                        {
                            Log("Gray card transaction, remove it otherwise it will block further transactions");
                            RemoveTransaction(cardTrx, _context);
                        }

                        //103, Operator card
                        if (cardTrx.PaymodeId == 103)
                        {
                            Log($"Found finished IC card transaction: Pump: {cardTrx.PumpNo}, Volume: {cardTrx.Vol}, Amount: {cardTrx.Mon}");

                            FdcTransaction fdcTransaction = new FdcTransaction();
                            fdcTransaction.Amount = cardTrx.Mon.Value;
                            fdcTransaction.Volumn = Convert.ToInt32(cardTrx.Vol.Value);
                            fdcTransaction.VolumeTotalizer = Convert.ToInt32(cardTrx.EndPumpId.Value);
                            fdcTransaction.Price = Convert.ToInt32(cardTrx.Prc.Value);
                            fdcTransaction.SequenceNumberGeneratedOnPhysicalPump = Convert.ToInt32(cardTrx.Gid);
                            fdcTransaction.Nozzle = new LogicalNozzle(cardTrx.PumpNo, 0, Convert.ToByte(NozzleId), 0);
                            fdcTransaction.Finished = true;

                            OnCurrentFuellingStatusChange?.Invoke(this, new FdcTransactionDoneEventArg(fdcTransaction));

                            RemoveTransaction(cardTrx, _context);
                        }
                        else if (cardTrx.PaymodeId == 100) //100, Customer card         定位卡问题
                        {
                            Log($"Found customer card transaction: Pump: {cardTrx.PumpNo}, Volume: {cardTrx.Vol}, Amount: {cardTrx.Mon}");

                            var submitResult = Task.Run(async () =>
                            {
                                var result = await SubmitTrxAsync(cardTrx, bindingBarcode);
                                return result;
                            });

                            Log($"submit transaction, result: {submitResult.Result}");

                            RemoveTransaction(cardTrx, _context);
                        }
                        else
                        {
                            //105:
                            //108:
                        }
                    }
                    else if (cardTrx != null && cardTrx.Mon == 0)
                    {
                        Log($"Zero amount transaction, Pump No: {cardTrx.PumpNo}, SeqNo: {cardTrx.SeqNo}, delete it!");
                        RemoveTransaction(cardTrx, _context);
                    }
                    else if (cardTrx != null && cardTrx.Gid != 0)
                    {
                        Log($"Trd Type: {cardTrx?.TrdType}");
                        RemoveTransaction(cardTrx, _context);
                    }

                    //Reacitivate the timer.
                    _timer.Start();
                }
            }
            catch (Exception ex)
            {
                Log($"Database operation: {ex}");
            }
        }

        /// <summary>
        ///00:正常卡交易
        ///01:灰卡交易
        ///02:解灰交易(解灰后即为正常交易)
        ///04:现金后台授权交易
        ///05:撤消授权卡交易
        ///06:非卡交易
        ///07:撤消授权非卡交易
        ///08:油价下载记录
        /// </summary>
        /// <param name="trdType"></param>
        /// <returns></returns>
        private bool IsTrdTypeAllowed(byte trdType)
        {
            if (trdType == 0 || trdType == 2 || trdType == 6)
                return true;

            return false;
        }

        private void RemoveTransaction(TCardtrx cardTrx, SpsDbContext context)
        {
            try
            {
                context.Remove(cardTrx);
                context.SaveChanges();
            }
            catch (Exception ex)
            {
                Log($"Exception in removing trx: {ex}");
            }
        }

        private async Task<bool> SubmitTrxAsync(TCardtrx cardTrx, int barcode)
        {
            var clientTrxInfo = new ClientTrxInfo
            {
                CardNo = cardTrx.CardNo,
                CurrentCardBalance = Convert.ToDecimal(cardTrx.CardBal.Value) / 100,
                UnitPrice = Convert.ToDecimal(cardTrx.Prc) / 100,
                Amount = Convert.ToDecimal(cardTrx.Mon) / 100,
                PayAmount = Convert.ToDecimal(cardTrx.RealMon) / 100,
                Volume = Convert.ToDecimal(cardTrx.Vol) / 100,
                PumpId = cardTrx.PumpNo,
                NozzleId = Convert.ToByte(cardTrx.NozNo),
                SiteNozzleNo = siteNozzleNo,
                Barcode = barcode,
                FuelingStartTime = cardTrx.Ttctime,
                FuelingFinishedTime = cardTrx.TtctimeEnd.Value,
                SeqNo = Convert.ToInt32(cardTrx.SeqNo)
            };

            var result = await submitter.SubmitTrxAsync(clientTrxInfo);
            return result;
        }

        public Task<bool> Stop()
        {
            if (_timer != null)
            {
                _timer.Dispose();
            }

            return Task.FromResult(true);
        }

        #endregion

        #region Event handlers

        public event EventHandler<FdcPumpControllerOnStateChangeEventArg> OnStateChange;
        public event EventHandler<FdcTransactionDoneEventArg> OnCurrentFuellingStatusChange;

        #endregion

        #region Methods, not useful

        public bool Authorize(byte logicalNozzleId)
        {
            return false;
        }

        public bool AuthorizeWithAmount(int moneyAmountWithoutDecimalPoint, byte logicalNozzleId)
        {
            return false;
        }

        public bool AuthorizeWithVolumn(int volumnWithoutDecimalPoint, byte logicalNozzleId)
        {
            return false;
        }

        public bool ChangeFuelPrice(int newPriceWithoutDecimalPoint, byte logicalNozzleId)
        {
            return false;
        }

        public bool FuelingRoundUpByAmount(int amount)
        {
            return false;
        }

        public bool FuelingRoundUpByVolumn(int volume)
        {
            return false;
        }

        public void OnFdcServerInit(Dictionary<string, object> parameters)
        {
            //the parameters don't make any sense for this fake pump handler.
        }

        public LogicalDeviceState QueryStatus()
        {
            return LogicalDeviceState.FDC_READY;
        }

        public Tuple<int, int> QueryTotalizer(byte logicalNozzleId)
        {
            return new Tuple<int, int>(-1, -1);
        }

        public bool ResumeFuelling()
        {
            return false;
        }

        public bool SuspendFuelling()
        {
            return false;
        }

        public bool UnAuthorize(byte logicalNozzleId)
        {
            return false;
        }

        public Task<LogicalDeviceState> QueryStatusAsync()
        {
            throw new NotImplementedException();
        }

        public Task<Tuple<int, int>> QueryTotalizerAsync(byte logicalNozzleId)
        {
            return Task.FromResult(new Tuple<int, int>(-1, -1));
        }

        public Task<bool> SuspendFuellingAsync()
        {
            throw new NotImplementedException();
        }

        public Task<bool> ResumeFuellingAsync()
        {
            throw new NotImplementedException();
        }

        public Task<bool> ChangeFuelPriceAsync(int newPriceWithoutDecimalPoint, byte logicalNozzleId)
        {
            throw new NotImplementedException();
        }

        public Task<bool> AuthorizeAsync(byte logicalNozzleId)
        {
            throw new NotImplementedException();
        }

        public Task<bool> UnAuthorizeAsync(byte logicalNozzleId)
        {
            throw new NotImplementedException();
        }

        public Task<bool> AuthorizeWithAmountAsync(int moneyAmountWithoutDecimalPoint, byte logicalNozzleId)
        {
            throw new NotImplementedException();
        }

        public Task<bool> AuthorizeWithVolumeAsync(int volumnWithoutDecimalPoint, byte logicalNozzleId)
        {
            throw new NotImplementedException();
        }

        public Task<bool> FuelingRoundUpByAmountAsync(int amount)
        {
            throw new NotImplementedException();
        }

        public Task<bool> FuelingRoundUpByVolumeAsync(int volume)
        {
            throw new NotImplementedException();
        }

        public async Task<bool> LockNozzleAsync(byte logicalNozzleId)
        {
            return false;
        }

        public async Task<bool> UnlockNozzleAsync(byte logicalNozzleId)
        {
            return false;
        }

        #endregion
    }
}