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.Diagnostics;
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 = maindbContextFactory.CreateDbContext();
        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();
                        db.ChangeTracker.Clear();

                        #endregion

                        await Task.Delay(100);

                    }

                }
            }
        }

    }
}
