using EVCB_OCPP.Domain;
using EVCB_OCPP.Domain.Models.Database;
using EVCB_OCPP.Packet.Features;
using EVCB_OCPP.Packet.Messages;
using EVCB_OCPP.WSServer.Message;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using OCPPServer.Protocol;
using Quartz;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EVCB_OCPP.WSServer.Jobs;

[DisallowConcurrentExecution]
public class ServerMessageJob : IJob
{
    public ServerMessageJob(
        ProtalServer protalServer,
        IConfiguration configuration,
        IDbContextFactory<MainDBContext> maindbContextFactory,
        ILogger<ServerMessageJob> logger)
    {
        this.protalServer = protalServer;
        this.maindbContextFactory = maindbContextFactory;
        this.logger = logger;
    }

    private readonly ProtalServer protalServer;
    private readonly IDbContextFactory<MainDBContext> maindbContextFactory;
    private readonly ILogger<ServerMessageJob> logger;

    public async Task Execute(IJobExecutionContext context)
    {
        try
        {
            await ExecuteTrigger();
        }
        catch (Exception ex)
        {
            logger.LogError("ServerMessageTrigger  Ex:{0}", ex.ToString());
        }
    }

    private async Task ExecuteTrigger()
    {
        protalServer.RemoveConfirmMessage();

        BasicMessageHandler msgAnalyser = new BasicMessageHandler();
        using (var db = await maindbContextFactory.CreateDbContextAsync())
        {
            var dateTimeNoew = DateTime.UtcNow;
            DateTime startDt = dateTimeNoew.AddSeconds(-30);
            DateTime dt = new DateTime(1991, 1, 1);
            DateTime currentTime = dateTimeNoew;
            var commandList = await db.ServerMessage.Where(c => c.ReceivedOn == dt && c.UpdatedOn == dt && c.CreatedOn >= startDt && c.CreatedOn <= currentTime).AsNoTracking().ToListAsync();
            var clientDic = protalServer.ClientDic;

            //處理主機傳送的有指令
            var cmdMachineList = commandList.Select(c => c.ChargeBoxId).Distinct().ToList();
            if (commandList.Count > 0)
            {
                // Console.WriteLine(string.Format("Now:{0} commandList Count:{1} ", DateTime.UtcNow.ToString("yyyy/MM/dd HH:mm:ss"), commandList.Count));
            }
            var resendList = protalServer.ResendMessage;
            foreach (var resendItem in resendList)
            {
                ClientData session;
                if (clientDic.TryGetValue(resendItem.ChargePointSerialNumber, out session))
                {
                    if (dateTimeNoew.Subtract(resendItem.SentOn).TotalSeconds > 1)
                    {
                        resendItem.SentTimes--;
                        resendItem.SentOn = dateTimeNoew;
                        protalServer.SendMsg(session, resendItem.SentMessage, string.Format("{0} {1}", resendItem.SentAction, "Request"), "");
                    }

                }

            }
            foreach (var charger_SN in cmdMachineList)
            {
                ClientData session;
                string uuid = string.Empty;
                if (clientDic.TryGetValue(charger_SN, out session))
                {
                    //logger.LogDebug(string.Format("charger_SN:{0} startDt:{1} CreatedOn:{2}", charger_SN, startDt.ToString("yyyy/MM/dd HH:mm:ss"), DateTime.UtcNow.ToString("yyyy/MM/dd HH:mm:ss")));

                    if (session.IsCheckIn && !session.ISOCPP20)
                    {
                        string rawRequest = string.Empty;

                        var cmdList = commandList.Where(c => c.ChargeBoxId == charger_SN).ToList();

                        var profiles = protalServer.Profiles;

                        foreach (var item in cmdList)
                        {
                            IRequest request = null;
                            Actions action = Actions.None;
                            Enum.TryParse(item.OutAction, out action);
                            Type _RequestType = null;

                            for (int i = 0; i < profiles.Count; i++)
                            {
                                var feature = profiles[i].GetFeaturebyAction(item.OutAction);

                                if (feature != null)
                                {
                                    _RequestType = feature.GetRequestType();
                                    break;
                                }
                            }

                            if (_RequestType != null && item.CreatedBy != "Destroyer")
                            {
                                request = JsonConvert.DeserializeObject(item.OutRequest, _RequestType) as IRequest;
                                uuid = session.queue.store(request);
                                rawRequest = BasicMessageHandler.GenerateRequest(uuid, item.OutAction, request);
                                protalServer.SendMsg(session, rawRequest, string.Format("{0} {1}", action, "Request"), "");
                            }


                            if (item.CreatedBy == "Destroyer")
                            {

                                if (_RequestType != null)
                                {
                                    request = Activator.CreateInstance(_RequestType) as IRequest;
                                    uuid = session.queue.store(request);
                                    rawRequest = BasicMessageHandler.GenerateDestroyRequest(uuid, item.OutAction, item.OutRequest);
                                    protalServer.SendMsg(session, rawRequest, string.Format("{0} {1}", action, "Request"), "");

                                }
                                else
                                {

                                    rawRequest = BasicMessageHandler.GenerateDestroyRequest(Guid.NewGuid().ToString(), item.OutAction, item.OutRequest);
                                    protalServer.SendMsg(session, rawRequest, string.Format("{0} {1}", action, "Request"), "");

                                }
                            }

                            protalServer.AddConfirmMessage(charger_SN, item.Id, item.SerialNo, item.OutAction, uuid, item.CreatedBy, rawRequest);

                            dateTimeNoew = DateTime.UtcNow;
                            #region 更新資料表單一欄位
                            var _UpdatedItem = new ServerMessage() { Id = item.Id, UpdatedOn = dateTimeNoew };
                            //db.Configuration.AutoDetectChangesEnabled = false;//自動呼叫DetectChanges()比對所有的entry集合的每一個屬性Properties的新舊值
                            //db.Configuration.ValidateOnSaveEnabled = false;// 因為Entity有些欄位必填,若不避開會有Validate錯誤
                            // var _UpdatedItem = db.ServerMessage.Where(x => x.Id == item.Id).FirstOrDefault();
                            db.ServerMessage.Attach(_UpdatedItem);
                            _UpdatedItem.UpdatedOn = dateTimeNoew;
                            db.Entry(_UpdatedItem).Property(x => x.UpdatedOn).IsModified = true;// 可以直接使用這方式強制某欄位要更新,只是查詢集合耗效能而己

                            await db.SaveChangesAsync();

                            #endregion

                            await Task.Delay(100);

                        }

                    }
                }
            }
        }
    }
}