using HslCommunication.Secs.Types;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using SlnMesnac.Business.@base;
using SlnMesnac.Config;
using SlnMesnac.Model.domain;
using SlnMesnac.Model.dto;
using SlnMesnac.Model.enums;
using SlnMesnac.Plc;
using SlnMesnac.Repository.service;
using SlnMesnac.Rfid;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TouchSocket.Core;

#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* 版权所有 (c) 2024 WenJY 保留所有权利。
* CLR版本：4.0.30319.42000
* 机器名称：LAPTOP-E0N2L34V
* 命名空间：SlnMesnac.Business
* 唯一标识：655997bf-2bdb-4426-bc49-6fd8def3b395
*
* 创建者：WenJY
* 电子邮箱：wenjy@mesnac.com
* 创建时间：2024-04-07 16:58:59
* 版本：V1.0.0
* 描述：
*
*--------------------------------------------------------------------
* 修改人：
* 时间：
* 修改说明：
*
* 版本：V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
namespace SlnMesnac.Business
{
    /// <summary>
    /// 设备生产管理业务类 -3F
    /// </summary>
    public class ProdMgmtBusiness: BaseBusiness
    {
        public readonly IMesProductPlanService _mesProductPlanService;

        public readonly IBasePalletInfoService _basePalletInfoService;

        public readonly IBaseRealTaskService _baseRealTaskService;
        
        private List<RealPalletTask> _palletTasks;

        private DebugConfig debugConfig = DebugConfig.Instance;

        public ProdMgmtBusiness(ILogger<ProdMgmtBusiness> logger, AppConfig appConfig, List<PlcAbsractFactory> plcFactories, List<RfidAbsractFactory> rfidFactories, IMesProductPlanService mesProductPlanService, IBasePalletInfoService basePalletInfoService, IBaseRealTaskService baseRealTaskService, List<RealPalletTask> palletTasks,IServiceProvider serviceProvider) : base(logger, appConfig, plcFactories, rfidFactories, serviceProvider)
        {
            _mesProductPlanService = mesProductPlanService;
            _basePalletInfoService = basePalletInfoService;
            _baseRealTaskService = baseRealTaskService;
            _palletTasks = palletTasks;
        }

        #region 委托事件
        /// <summary>
        /// 刷新计划列表
        /// </summary>
        public delegate void RefreshProdPlanList(List<MesProductPlanDto> list);
        public event RefreshProdPlanList? RefreshProdPlanListEvent;

        /// <summary>
        /// 刷新执行计划
        /// </summary>
        public delegate void RefreshProdPlanExec(MesProductPlan productPlan);
        public event RefreshProdPlanExec? RefreshProdPlanExecEvent;

        /// <summary>
        /// 投料确认,validType确认类型：1-读取失败重新读取；2-校验失败是否投料
        /// </summary>
        public delegate bool MatPutInValid(int validType, MesProductPlan productPlan, string materialName, string msg);
        public event MatPutInValid? MatPutInValidEvent;

        #endregion


        public void InitProdPlan()
        {
            RefreshMesProdList();
            Task.Run(() => { ProdPlanExecHandle(); });
            Task.Run(() => { MaterialPutInCheck(); });
            
        }

        /// <summary>
        /// 刷新计划执行列表
        /// </summary>
        private void RefreshMesProdList()
        {
            try
            {
                var info = _mesProductPlanService.GetPlanDtos();
                RefreshProdPlanListEvent?.Invoke(info);
            }
            catch (Exception ex)
            {
                _logger.LogError($"MES生产计划获取异常：{ex.Message}");
            }
        }

        /// <summary>
        /// 计划执行，叫料逻辑处理
        /// </summary>
        private void ProdPlanExecHandle()
        {
            //每5秒执行一次，获取是否有已开始的计划，获取已开始的计划下发给WCS进行叫料，获取货架到位信息进行投料校验，持续获取设备要料信号
            while (true)
            {
                try
                {
                    var plc = base.GetPlcByKey("plc");

                    if (plc == null)
                    {
                        //throw new ArgumentException($"PLC连接信息为空");
                        RefreshMessage($"PLC连接信息为空");
                    }

                    MesProductPlan prodPlan = _mesProductPlanService.GetStartedProdPlan(out MesProductPlanDto productPlanDto);
                    if (prodPlan == null || productPlanDto == null)
                    {
                        //throw new ArgumentException($"未获取到需要执行的生产计划");
                        RefreshMessage($"未获取到需要执行的生产计划");
                        Thread.Sleep(5000);
                        continue;
                    }

                    if (!plc.readBoolByAddress(GetPlcAddressByConfigKey("设备叫料")))
                    {
                        RefreshMessage("等待设备叫料信号触发......");
                        Thread.Sleep(5000);
                        continue;
                    }
                    BaseRealTask task =  _baseRealTaskService.GetExeTask();
                    if (task!=null)  {
                        RefreshMessage("已经叫料，请等待AGV送料......");
                       
                        Thread.Sleep(5000);
                        continue;
                    }


                    RefreshMessage("设备要料信号触发成功");

                    RefreshProdPlanExecEvent?.Invoke(prodPlan);
                    
                   // string palletCode = GetPalletInfoByTask();

                    RefreshMessage($"执行计划:{prodPlan.PlanCode};计划数量:{Math.Round(prodPlan.PlanAmount,2)};完成数量:{Math.Round(prodPlan.CompleteAmount,2)};发起叫料申请等待AGV送料......");

                    //进行叫料
                    if (ApplyDeliveryHandle(productPlanDto.MaterialId.ToString()))
                    {
                        RefreshMessage("根据计划自动申请叫料成功");
                        #region  本地创建一个叫料任务
                        BaseRealTask realTask = new BaseRealTask();
                        realTask.PlanCode = prodPlan.PlanCode;
                        realTask.PlanAmount = prodPlan.PlanAmount;
                        realTask.PlanComplete = prodPlan.CompleteAmount;
                        realTask.MaterialId = prodPlan.MaterialId;
                        realTask.Status = 1;
                        realTask.CreateTime = DateTime.Now;
                        realTask.UpdateTime = DateTime.Now;
                        _baseRealTaskService.InsertTask(realTask);
                        #endregion
                        plc.writeBoolByAddress(GetPlcAddressByConfigKey("设备叫料"), false);
                    }
                    else
                    {
                        RefreshMessage("根据计划自动申请叫料失败");
                    }
                    
                }
                catch (Exception e)
                {
                    _logger.LogError($"计划执行逻辑处理异常：{e.Message}");
                }
                Thread.Sleep(5000);
            }
        }

        /// <summary>
        /// 投料校验:轮询物料到位信号
        /// </summary>
        /// <exception cref="ArgumentException"></exception>
        public void MaterialPutInCheck()
        {
            bool result = false;
            while (true)
            {
                //mes计划
                MesProductPlan prodPlan = null;
                //本地叫料任务
                BaseRealTask localPlan = null;
                try
                {
                    var plc = base.GetPlcByKey("plc");

                    if (plc == null)
                    {
                        _logger.LogInformation("读取物料到位信号,PLC连接信息为空......");
                        Thread.Sleep(3000);
                        continue;
                    }

                    if (!plc.readBoolByAddress(GetPlcAddressByConfigKey("物料到位")))
                    {
                        _logger.LogInformation("等待物料到位信号触发......");
                        Thread.Sleep(3000);
                        continue;
                    }

                    // TODO  时间校验
                    bool timeCheck = JudgeProductTime();
                    if (!timeCheck)
                    {
                        RefreshMessage($"收到物料到位信号,但是距离上次投料不满20min,禁止投料");
                        Thread.Sleep(1000 * 15);
                        continue;
                    }

                    //TODO 调用调度接口，判断AGV小车是否已经离开到位，否则不允许投料



                    //TODO根据本地叫料计划查询对应的mes计划，完成
                    prodPlan = _mesProductPlanService.GetStartedProdPlan(out MesProductPlanDto productPlanDto);

                    if (productPlanDto == null || prodPlan == null)
                    {
                        RefreshMessage($"收到物料到位信号,未获取到正在执行的生产计划,请开始执行计划");
                        
                        Thread.Sleep(5000);
                        continue;
                    }

                    // 获取本地执行中的叫料任务,完成本地任务
                    localPlan = _baseRealTaskService.GetExeTask();
                    if (localPlan == null)  
                    {
                        
                    }

                    #region 物料校验   -- TODO 如果计划暂停，开启新计划，校验当前滞留物料是与前一条计划判断，还是当前开始的计划判断
                    //RefreshMessage("物料到位信号触发成功,读取托盘RFID信息进行投料校验");


                    //ReadEpcStrByRfidKey("test", out string epcStr);
                    //RefreshMessage($"投料校验RFID标签读取成功，标签信息：{epcStr}");

                   
                    ////获取物料托盘上的物料信息
                    //BasePalletInfo palletInfo = _basePalletInfoService.GetPalletInfoByPalletCode(epcStr);
                    //if (palletInfo == null)
                    //{
                    //    result = MatPutInValidTriggerEvent(2, prodPlan, string.Empty, "投料校验失败,未获取到托盘信息，是否继续投料");
                    //}
                    //else
                    //{
                    //    if (palletInfo.MaterialId != prodPlan.MaterialId)
                    //    {
                    //        result = MatPutInValidTriggerEvent(2, prodPlan, string.Empty, "投料校验失败,物料信息不匹配，是否继续投料");
                    //    }
                    //    else
                    //    {
                    //        result = true;
                    //    }
                    //}
                    //if (result)
                    //{
                    //    bool isRes = _basePalletInfoService.UnBindPalletInfo(palletInfo);
                    //    RefreshMessage($"物料校验通过，托盘信息解绑{(isRes ? "成功" : "失败")}");
                    //}
                    #endregion

                }
                catch (Exception e)
                {
                    _logger.LogInformation($"投料校验异常:{e.Message}");
                    // TODO修改成传入本地计划查询出的mes计划
                   // result = MatPutInValidTriggerEvent(2, prodPlan, string.Empty, $"投料校验异常:{e.Message}，是否继续投料");
                }
                if (prodPlan != null)
                {
                    // TODO修改成传入本地计划查询出的mes计划，停止计划导致的滞留物料归属哪个订单计划对应更新
                    PutInResutlHandle(prodPlan, result);

                    // 完成本地计划
                    if(localPlan!= null)
                    {
                        localPlan.Status = 2;
                        localPlan.UpdateTime = DateTime.Now;
                        _baseRealTaskService.Update(localPlan);
                    }
                }

                Thread.Sleep(3000);
            }
        }

        /// <summary>
        /// 投料结果处理
        /// </summary>
        /// <param name="prodPlan"></param>
        /// <param name="isRes"></param>
        private void PutInResutlHandle(MesProductPlan prodPlan,bool isRes)
        {
            try
            {
                if (isRes)
                {
                    RefreshMessage("投料校验完成，允许投料，更新生产计划");
                    prodPlan.CompleteAmount += 1;
                    if (prodPlan.CompleteAmount >= prodPlan.PlanAmount)
                    {
                        prodPlan.PlanStatus = PlanStatusEnum.已完成;
                    }

                    _mesProductPlanService.Update(prodPlan);

                    RefreshMesProdList();
                }
                else
                {
                    RefreshMessage("投料校验失败，人工确认不许投料");
                }
            }catch(Exception e)
            {
                _logger.LogInformation($"投料结果处理异常:{e.Message}");
            }
        }

        /// <summary>
        /// 投料校验失败，人工确认是否继续投料
        /// </summary>
        /// <param name="validType"></param>
        /// <param name="productPlan"></param>
        /// <param name="materialName"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        private bool MatPutInValidTriggerEvent(int validType, MesProductPlan productPlan, string materialName, string msg)
        {
            return MatPutInValidEvent.Invoke(validType, productPlan, materialName, msg);
        }

        /// <summary>
        /// 申请叫料，TODO等待调度接口
        /// </summary>
        /// <param name="materialCode"></param>
        /// <param name="result"></param>
        /// <exception cref="InvalidOperationException"></exception>
        public bool ApplyDeliveryHandle(string materialCode)
        {
            bool result = false;
            try
            {
                var plc = base.GetPlcByKey("plc");

                if (plc != null)
                {
                    if (string.IsNullOrEmpty(materialCode))
                    {
                        throw new ArgumentException("申请叫料处理异常:物料参数为空");
                    }


                   // TODO -- 修改为调用wcs接口,参数为申请单id,同时本地生成一个叫料任务，任务状态为执行中
                   // plc.writeInt16ByAddress(base.GetPlcAddressByConfigKey("WCS叫料"), 1);
                   // plc.writeStringByAddress(base.GetPlcAddressByConfigKey("物料型号"), materialCode);


                    result = true;
                }

            }
            catch (Exception e)
            {
                throw new InvalidOperationException($"申请叫料处理异常:{e.Message}");
            }

            return result;
        }

        /// <summary>
        /// 更新生产计划
        /// </summary>
        /// <param name="planInfo"></param>
        public void UpdateProdPlan(MesProductPlanDto planInfo)
        {
            try
            {
                if (planInfo == null)
                {
                    throw new ArgumentException("更新生产计划异常:参数为空");
                }

                var info = _mesProductPlanService.GetProdPlanByPlanCode(planInfo.PlanCode);

                if(info == null)
                {
                    throw new ArgumentException("更新生产计划异常:根据计划编号获取计划信息为空");
                }

                info.PlanStatus = planInfo.PlanStatus;
                if (!_mesProductPlanService.UpdateProdPlan(info))
                {
                    throw new InvalidOperationException("生产计划更新失败");
                }

                RefreshMesProdList();
            }
            catch (Exception e)
            {
                throw new InvalidOperationException($"生产计划更新异常:{e.Message}");
            }
        }


        ///
        /// <summary
        /// 距离上次投料时间合格判断
        /// </summary
        private bool JudgeProductTime()
        {
            try
            {
                int IntervalMin = int.Parse(GetPlcAddressByConfigKey("投料时间间隔"));   // 单位分钟
                if (debugConfig.LastTime != "")
                {

                    DateTime lastTime = DateTime.Parse(debugConfig.LastTime);

                    TimeSpan timeDifference = DateTime.Now - lastTime;

                    if (timeDifference.TotalMinutes > IntervalMin)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }

                }
                else
                {
                    return true;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"距离上次投料时间合格判断异常JudgeProductTime：{ex.Message}");
                return false;
               
            }
        }
    }
}
