using Masuit.Tools;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json;
using SlnMesnac.Config;
using SlnMesnac.Extensions;
using SlnMesnac.Model.domain;
using SlnMesnac.Model.dto.taskType;
using SlnMesnac.Model.dto.webapi;
using SlnMesnac.Plc;
using SlnMesnac.Rfid;
using SlnMesnac.Serilog;
using SlnMesnac.WCS.Global;
using SlnMesnac.WCS.Library;
using SlnMesnac.WCS.WCS;
using SqlSugar;
using System.Timers;

namespace SlnMesnac.WCS
{
    public class MainCentralControl
    {
        private IHost host;

        //删除日志定时器
        private System.Timers.Timer deleteLogTimer;

        //plc心跳交互定时器
        private System.Timers.Timer plcHeartTimer;

        // 新增一个标志位，用于表示方法是否正在执行
        private bool isExecuting = false;

        private readonly SerilogHelper _logger;
        private IServiceProvider serviceProvider;
        private ISqlSugarClient SqlSugarClient;

        private BearAgv bearAgv;
        #region 变量定义
        public List<PlcAbsractFactory> plcList;

        //容器里面的读写器集合
        public List<RfidAbsractFactory> rfidList;

        #endregion 变量定义
        
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="host"></param>
        public MainCentralControl(IHost _host)
        {
            host = _host;
            SqlSugarClient = host.Services.GetRequiredService<ISqlSugarClient>();
            _logger = host.Services.GetRequiredService<SerilogHelper>();
            plcList = host.Services.GetRequiredService<List<PlcAbsractFactory>>();
            rfidList = host.Services.GetRequiredService<List<RfidAbsractFactory>>();
            InitCacheData();
        }

        /// <summary>
        /// 加载缓存数据
        /// </summary>
        private void InitCacheData()
        {
            //加载plc地址信息
            StaticData.BaseConfigList = SqlSugarClient.Queryable<BaseConfigInfo>().ToList();
            StaticData.WmsMachineInfos = SqlSugarClient.Queryable<WmsMachineInfo>().ToList();
            StaticData.WcsAgvCodeMaps = SqlSugarClient.Queryable<WcsAgvCodeMap>().ToList();
            #region 定时删除日志
            //DeleteLogsLogic();
            //deleteLogTimer = new System.Timers.Timer(24 * 60 * 60 * 1000);
            //deleteLogTimer.Elapsed += OnTimedEvent;
            //deleteLogTimer.AutoReset = true;
            //deleteLogTimer.Enabled = true;
            #endregion 定时删除日志
            #region 定时写入心跳
            plcHeartTimer = new System.Timers.Timer(1000);
            plcHeartTimer.Elapsed += OnTimedPlcHeartEvent;
            plcHeartTimer.AutoReset = true;
            plcHeartTimer.Enabled = true;
            #endregion 定时写入心跳
        }

        /// <summary>
        /// 启动程序
        /// </summary>
        public void Start()
        {
            StartCheckStatus();
            GetAgvStatus();
            DeleteTaskLogic();

            //根据条件创建任务
            CreateTaskByRecord createTaskByRecord = new CreateTaskByRecord(host.Services);
            createTaskByRecord.StartPoint();

            _logger.Agv("WCS调度程序启动成功!");

            //背负式agv执行任务
            bearAgv = new BearAgv(host.Services);
            bearAgv.StartPoint();
        }

        #region

        /// <summary>
        /// 检查设备状态自动重连
        /// </summary>
        private void StartCheckStatus()
        {
            //数据库里面的读写器集合，仅用来更新状态
            List<BaseRfidInfo> rfidDbList = SqlSugarClient.Ado.Context.CopyNew().Queryable<BaseRfidInfo>().ToList();
            List<WcsBaseEquip> agvEquipList = SqlSugarClient.Ado.Context.CopyNew().Queryable<WcsBaseEquip>().Where(it => it.EquipType == 1).ToList();
            Task.Run(async () =>
           {
               while (true)
               {
                   try
                   {
                       #region PLC状态
                       foreach (var plc in plcList)
                       {
                           PlcConfig? plcConfig = host.Services.GetService<AppConfig>().plcConfig.FirstOrDefault(x => x.plcKey == plc.ConfigKey);
                           if (plc != null && plc.IsConnected)
                           {
                               plc.IsConnected = plc.readHeartByAddress("M100");  //DB100.DBX0.0
                               if (!plc.IsConnected)
                               {
                                   plc.IsConnected = await plc.ConnectAsync(plcConfig.plcIp, plcConfig.plcPort);
                               }
                           }
                           else
                           {
                               if (plcConfig != null)
                               {
                                   bool result = await plc.ConnectAsync(plcConfig.plcIp, plcConfig.plcPort);
                                   plc.IsConnected = result;
                               }
                           }
                       }

                       #endregion PLC状态

                       #region RFID状态
                       foreach (var rfid in rfidList)
                       {
                           if (rfid.ConfigKey == "2#Transplant")
                           {
                           }
                           bool status = rfid.GetOnlineStatus();
                           if (!status)
                           {
                               status = await rfid.ConnectAsync(rfid.ip, rfid.port);
                           }
                           //状态同步数据库
                           BaseRfidInfo? rfidDb = rfidDbList.FirstOrDefault(x => x.EquipIp == rfid.ip);
                           if (rfidDb != null)
                           {
                               rfidDb.IsOnline = status ? 1 : 0;
                           }
                       }
                       lock (string.Empty)
                       {
                           try
                           {
                               SqlSugarClient.Ado.Context.CopyNew().Updateable(rfidDbList).UpdateColumns(it => new { it.IsOnline }).ExecuteCommand();
                           }
                           catch (Exception ex)
                           {
                               _logger.Error($"更新RFID状态异常:{ex.Message}",ex);
                           }
                       }
                       #endregion RFID状态

                       //色粉库位有未取走色粉声光报警
                       ListeningTonerLocation();

                       //大托盘库位有未取走色粉声光报警
                       ListeningBigPlateLocation();
                   }
                   catch (Exception ex)
                   {
                       _logger.Error($"监听设备状态异常:{ex.Message}");
                   }
                   await Task.Delay(1000 * 10);
               }
           });
        }

        /// <summary>
        /// 获取agv状态
        /// </summary>
        private void GetAgvStatus()
        {
            List<WcsBaseEquip> agvEquipList = SqlSugarClient.Queryable<WcsBaseEquip>().Where(it => it.EquipType == 1).ToList();
            Task.Run(async () =>
            {
                while (true)
                {
                    try
                    {
                        //实时获取agv状态
                        MessageSynchronousLogic(agvEquipList);
                    }
                    catch (Exception ex)
                    {
                        _logger.Error($"获取agv状态异常:{ex.Message}");
                    }
                    await Task.Delay(1000 * 30);
                }
            });
        }

        #endregion

        /// <summary>
        /// 监听色粉库位是否色粉未从料想取出，有的话通知plc声光报警
        /// </summary>
        private void ListeningTonerLocation()
        {
            try
            {
                PlcAbsractFactory? workShop3Plc = plcList.FirstOrDefault(x => x.ConfigKey == "workShop3Plc");
                // 构建查询并执行 Any 方法
                bool hasRecords = SqlSugarClient.Ado.Context.CopyNew().Queryable<WcsBaseEquip>()
                    .InnerJoin<WmsPalletInfo>((wbe, wpi) => wbe.ContainerCode == wpi.PalletInfoCode
                                                         && !string.IsNullOrEmpty(wbe.ContainerCode)
                                                         && wpi.TonerFlag == 1 && wbe.EquipType == 7).Any();
                if (workShop3Plc != null && workShop3Plc.IsConnected)
                {
                    if (hasRecords)
                    {
                        workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#异常色粉未处理提示"), true);
                    }
                    else
                    {
                        workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#异常色粉未处理提示"), false);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.Error($"监听色粉库位异常:{ex.Message}");
            }
        }

        private void ListeningBigPlateLocation()
        {
            try
            {
                PlcAbsractFactory? workShop3Plc = plcList.FirstOrDefault(x => x.ConfigKey == "workShop3Plc");
                bool hasRecords = SqlSugarClient.Ado.Context.CopyNew().Queryable<WmsBaseLocation>()
                    .InnerJoin<WmsPalletInfo>((wbe, wpi) => wbe.ContainerCode == wpi.PalletInfoCode
                                                         && !string.IsNullOrEmpty(wbe.ContainerCode)
                                                         && wpi.Amount > 0 && wbe.MachineId > 0 && wbe.MachineId < 12).Any();
                bool hasRecords12 = SqlSugarClient.Ado.Context.CopyNew().Queryable<WmsBaseLocation>()
                    .InnerJoin<WmsPalletInfo>((wbe, wpi) => wbe.ContainerCode == wpi.PalletInfoCode
                                                         && !string.IsNullOrEmpty(wbe.ContainerCode)
                                                         && wpi.Amount > 0 && wbe.MachineId == 12).Any();
                if (workShop3Plc != null && workShop3Plc.IsConnected)
                {
                    if (hasRecords)
                    {
                        workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#机台校验失败提示"), true);
                    }
                    else
                    {
                        workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#机台校验失败提示"), false);
                    }

                    if (hasRecords12)
                    {
                        workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#12号机台未校验提示"), true);
                    }
                    else
                    {
                        workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#12号机台未校验提示"), false);
                    }
                }
            }
            catch(Exception ex)
            {
                _logger.Error($"监听大托盘库位异常:{ex.Message}");
            }
        } 

        /// <summary>
        /// agv状态实时刷新
        /// </summary>
        private void MessageSynchronousLogic(List<WcsBaseEquip> agvEquipList)
        {
            try
            {

                //List<WcsAgvStatus> agvList = SqlSugarClient.Queryable<WcsAgvStatus>().ToList();
                List<WcsAgvStatus> agvList = SqlSugarClient.Ado.Context.CopyNew().Queryable<WcsAgvStatus>().ToList();
                var data = new
                {
                    reqCode = StaticData.SnowId.NextId(),
                    mapCode = "AA",
                };
                string result = HttpHelper.SendPostMessage(agvEquipList.First().ServerIp, agvEquipList.First().ServerPort, "rcms-dps/rest/queryAgvStatus", data.ToJsonString());
                var reponse = JsonConvert.DeserializeObject<AgvStatusDto>(result);
                if (reponse != null && reponse.code == "0")
                {
                    foreach (var agv in agvList)
                    {
                        CardStatus? agvCard = reponse.data.FirstOrDefault(x => x.robotCode == agv.RobotCode);
                        if (agvCard != null)
                        {
                            agv.Battery = agvCard.battery;
                            agv.ExclType = agvCard.exclType;
                            agv.Online = agvCard.online;
                            agv.PosX = agvCard.posX;
                            agv.PosY = agvCard.posY;
                            agv.RobotDir = agvCard.robotDir;
                            agv.Speed = agvCard.speed;
                            agv.Status = agvCard.status;
                            WcsAgvCodeMap? wcsAgvCodeMap = StaticData.WcsAgvCodeMaps.FirstOrDefault(x => x.AgvCode == long.Parse(agv.Status));
                            if (wcsAgvCodeMap != null)
                            {
                                agv.StatusDetail = wcsAgvCodeMap.agv_display_content;
                            }
                            agv.Stop = agvCard.stop;
                            agv.Timestamp = DateTime.Now;
                        }
                    }

                    //TaskHandleByAgv(agvList);
                    //SqlSugarClient.Updateable(agvList).ExecuteCommand();
                    SqlSugarClient.Ado.Context.CopyNew().Updateable(agvList).ExecuteCommand();
                }
            }
            catch (Exception ex)
            {
                _logger.Info($"同步agv状态信息异常:{ex.Message},{ex.StackTrace.ToJsonString()}");
            }
        }

        public void TaskHandleByAgv(List<WcsAgvStatus> agvInfos)
        {
            if (agvInfos == null || agvInfos.Count == 0)
            {
                return;
            }

            var targetPoint = new { X = 142430, Y = 108540 };

            var agvInfo = agvInfos.OrderBy(agv => CalculateDistance(double.Parse(agv.PosX), double.Parse(agv.PosY), targetPoint.X, targetPoint.Y)).First();
            
            _logger.Agv($"=====>>>>离接驳位最近的设备为:{agvInfo.RobotCode}");
            
            //判断当前小车有没有送空箱任务,下发PLC触发取料信号
            if (agvInfo.StatusDetail.Contains("待机模式") || agvInfo.StatusDetail.Contains("空闲"))
            {
                _logger.Agv($"当前空闲车辆{agvInfo.RobotCode};离取料点最近，优先进行取料，锁定线体，下发PLC触发取料信号");
            }
            else
            {
                _logger.Agv($"当前没有空闲小车离取料点最近，等线体空闲后进行取料");
                bearAgv.workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#空箱进缓存请求"),true);
                bool flag = bearAgv.workShop3Plc.readBoolByAddress(StaticData.GetPlcAddress("3#空箱进缓存允许"));
                if (flag == true)
                {
                    //线体空闲了，可以锁定进行取料
                    _logger.Agv($"线体空闲了，可以锁定使用远处小车进行取料");
                }
            }
        }
        
        private static double CalculateDistance(double x1, double y1, double x2, double y2)
        {
            return Math.Sqrt(Math.Pow(x2 - x1, 2) + Math.Pow(y2 - y1, 2));
        }
        
        #region 删除任务  -- 已经下发的任务，需要通知agv取消

        /// <summary>
        /// 定时监测是否有待删除任务，完成删除
        /// </summary>
        private void DeleteTaskLogic()
        {
            Task.Run(() =>
            {
                var sqlClient = SqlSugarClient.Ado.Context.CopyNew();
                List<WcsBaseEquip> agvEquipList = SqlSugarClient.Ado.Context.CopyNew().Queryable<WcsBaseEquip>().Where(it => it.EquipType == 1).ToList();
                while (true)
                {
                    try
                    {
                        List<WcsTask> wcsTasks = SqlSugarClient.Ado.Context.CopyNew().Queryable<WcsTask>().Where(t => t.IsDelete == 1 || t.IsDelete == 2).ToList();
                        foreach (var item in wcsTasks)
                        {
                            WcsBaseEquip agvEquip = agvEquipList.First();
                            if (!string.IsNullOrEmpty(item.TaskCode))
                            {
                                var cancelTask = new
                                {
                                    reqCode = StaticData.SnowId.NextId().ToString(),
                                    taskCode = item.TaskCode
                                };

                                string reponse = HttpHelper.SendPostMessage(agvEquip.ServerIp, agvEquip.ServerPort, "rcms/services/rest/hikRpcService/cancelTask", cancelTask.ToJsonString());
                                var result = JsonConvert.DeserializeObject<ReponseMessage>(reponse);
                                if (result != null && result.code == "0")
                                {
                                    #region 解锁起点库位与终点库位
                                    //根据任务类型,如果是大料箱，解锁wmsbaseLocation
                                    List<int> bigType = new List<int>() { 1, 3, 8, 10 };
                                    if (bigType.Contains(item.TaskType))
                                    {
                                        List<WmsBaseLocation>? wmsBaseLocations = SqlSugarClient.Ado.Context.CopyNew().Queryable<WmsBaseLocation>().Where(x => x.LocationCode == item.CurrPointNo || x.LocationCode == item.EndPointNo).ToList();
                                        if (wmsBaseLocations != null && wmsBaseLocations.Count > 0)
                                        {
                                            wmsBaseLocations.ForEach(x => x.LocationStatus = 0);
                                            SqlSugarClient.Ado.Context.CopyNew().Updateable(wmsBaseLocations).ExecuteCommand();
                                        }
                                    }
                                    else  //如果是小料箱，解锁wcsbaseEquip
                                    {
                                        List<WcsBaseEquip> equips = SqlSugarClient.Ado.Context.CopyNew().Queryable<WcsBaseEquip>().Where(x => x.AgvPositionCode == item.CurrPointNo || x.AgvPositionCode == item.EndPointNo).ToList();
                                        if (equips != null && equips.Count > 0)
                                        {
                                            equips.ForEach(x => x.EquipStatus = 0);
                                            SqlSugarClient.Ado.Context.CopyNew().Updateable(equips).ExecuteCommand();
                                        }
                                    }
                                    #endregion
                                    SqlSugarClient.Ado.Context.CopyNew().Deleteable<WcsTask>(item).ExecuteCommand();
                                    _logger.Agv($"任务id:{item.Id},任务名:{item.TaskName}已删除");

                                    //
                                    if (!string.IsNullOrEmpty(item.NextPointNo) && item.TaskStatus >= 2 && item.IsDelete == 2)  //有执行任务的agv编号,并且需要去异常库位
                                    {
                                        WmsBaseLocation? endLocation = SqlSugarClient.Ado.Context.CopyNew().Queryable<WmsBaseLocation>().First(x => x.LocationCode == "3066" && x.LocationStatus == 0 && string.IsNullOrEmpty(x.ContainerCode));
                                        if (endLocation != null)
                                        {
                                            WcsTask task = new WcsTask();
                                            task.TaskType = StaticTaskType.ExceptionTask;
                                            task.CurrPointNo = "ExceptionStart";
                                            task.NextPointNo = item.NextPointNo; //指定agv
                                            task.EndPointNo = endLocation.AgvPositionCode;
                                            task.TaskStatus = 0;
                                            task.CreatedTime = DateTime.Now;
                                            task.CreatedBy = "wcs";
                                            task.TaskName = "前往异常库位移库任务";
                                            task.PalletInfoCode = item.PalletInfoCode;
                                            sqlClient.AsTenant().BeginTran();
                                            try
                                            {
                                                int id = SqlSugarClient.Ado.Context.CopyNew().Insertable(task).ExecuteReturnIdentity();
                                                WcsTaskLog wcsTaskLog = CoreMapper.Map<WcsTaskLog>(task);
                                                wcsTaskLog.Id = id;
                                                //锁定库位
                                                endLocation.LocationStatus = 1;
                                                SqlSugarClient.Ado.Context.CopyNew().Updateable(endLocation).ExecuteCommand();
                                                SqlSugarClient.Ado.Context.CopyNew().Insertable(wcsTaskLog).ExecuteCommand();
                                                sqlClient.AsTenant().CommitTran();
                                                _logger.Agv($"生成{task.TaskName},起点:{task.CurrPointNo},终点:{task.EndPointNo}");
                                            }
                                            catch (Exception ex)
                                            {
                                                _logger.Error($"生成移库任务提交事务异常:{ex.Message}");
                                                sqlClient.AsTenant().RollbackTran();
                                            }
                                        }
                                        else
                                        {
                                            _logger.Error("想要创建异常库位任务，但是异常库位不可用或已经有托盘");
                                        }
                                    }
                                }
                                else
                                {
                                    _logger.Error($"任务id:{item.Id},任务名:{item.TaskName}删除失败，agv接口回复:{result.message}");
                                }
                            }
                            else
                            {
                                sqlClient.Deleteable<WcsTask>(item).ExecuteCommand();
                                _logger.Agv($"任务id:{item.Id},任务名:{item.TaskName}已删除");
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        _logger.Error($"任务删除异常:{ex.Message}");
                    }
                    Thread.Sleep(2000);
                }
            });
        }

        #endregion 删除任务  -- 已经下发的任务，需要通知agv

        #region 定时删除日志处理

        /// <summary>
        /// 定时器触发的事件处理方法
        /// </summary>
        /// <param name="source"></param>
        /// <param name="e"></param>
        private void OnTimedEvent(Object source, ElapsedEventArgs e)
        {
            DeleteLogsLogic();
        }

        /// <summary>
        /// 清除日志文件
        /// </summary>
        private void DeleteLogsLogic()
        {
            try
            {
                // 获取当前程序运行目录下的 Log 文件夹
                string logPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");

                // 检查 Log 文件夹是否存在
                if (!Directory.Exists(logPath))
                {
                    // 如果 Log 文件夹不存在，可以选择记录日志或者直接返回
                    Console.WriteLine($"Log 文件夹 {logPath} 不存在。");
                    return;
                }

                // 获取 Log 文件夹下的所有子文件夹
                string[] logDirectories = Directory.GetDirectories(logPath);

                foreach (string directoryPath in logDirectories)
                {
                    string directoryName = Path.GetFileName(directoryPath);
                    if (!string.IsNullOrEmpty(directoryName) && DateTime.TryParse(directoryName, out DateTime directoryDate))
                    {
                        // 检查文件夹日期是否早于当前日期三天之前
                        if (directoryDate < DateTime.Now.AddDays(-3))
                        {
                            try
                            {
                                // 删除符合条件的文件夹
                                Directory.Delete(directoryPath, true);
                                Console.WriteLine($"已删除文件夹: {directoryPath}");
                            }
                            catch (Exception ex)
                            {
                                // 记录删除文件夹时的异常信息
                                Console.WriteLine($"删除文件夹 {directoryPath} 时出错: {ex.Message}");
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                // 记录整体操作的异常信息
                Console.WriteLine($"执行删除日志逻辑时出错: {ex.Message}");
            }
        }

        #endregion

        #region 定时plc发送心跳脉冲
        private bool heartFlag = true;

        /// <summary>
        /// 定时器触发的事件处理方法
        /// </summary>
        /// <param name="source"></param>
        /// <param name="e"></param>
        private void OnTimedPlcHeartEvent(Object source, ElapsedEventArgs e)
        {
            // 检查方法是否正在执行
            if (isExecuting)
            {
                return;
            }

            try
            {
                // 设置标志位为 true，表示方法正在执行
                isExecuting = true;

                PlcAbsractFactory? plc3 = plcList.FirstOrDefault(x => x.ConfigKey == "workShop3Plc");
                PlcAbsractFactory? plc2 = plcList.FirstOrDefault(x => x.ConfigKey == "workShop2Plc");
                if (plc3 != null && plc3.IsConnected)
                {
                    plc3.writeBoolByAddress("DB100.DBX84.0", heartFlag);
                    heartFlag = !heartFlag;
                }
                if (plc2 != null && plc2.IsConnected)
                {
                    plc2.writeBoolByAddress("DB100.DBX24.0", heartFlag);
                    heartFlag = !heartFlag;
                }
            }
            finally
            {
                isExecuting = false;
            }
        }

        #endregion
    }
}