using HslCommunication.Secs.Types;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Npgsql.Replication.TestDecoding;
using SlnMesnac.Business.@base;
using SlnMesnac.Common;
using SlnMesnac.Common.Model;
using SlnMesnac.Config;
using SlnMesnac.Model.domain;
using SlnMesnac.Model.dto;
using SlnMesnac.Model.enums;
using SlnMesnac.Plc;
using SlnMesnac.Repository.service;
using SlnMesnac.Repository.service.Impl;
using SlnMesnac.Rfid;
using SlnMesnac.Rfid.Dto;
using SlnMesnac.TouchSocket;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Transactions;
using TouchSocket.Core;
using TouchSocket.Sockets;

#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 IMesPrdBarCodeService _mesPrdBarCodeService;

        public readonly IBaseRealTaskService _baseRealTaskService;

        public readonly IWmsOutStockService _wmsOutStockService;

        private readonly ConfigInfoBusiness _configInfoBusiness;

        /// <summary>
        /// 消息弹框提示
        /// </summary>
        /// <param name="msg"></param>
        public delegate void MsgHandler(string message, int msgtype, int outTime = 3);
        public static event MsgHandler MsgHandlerEvent;


        private ISqlSugarClient sqlSugarClient;
        private DebugConfig debugConfig = DebugConfig.Instance;

        public ProdMgmtBusiness(ISqlSugarClient _sqlSugarClient, IWmsOutStockService wmsOutStockService, ILogger<ProdMgmtBusiness> logger, AppConfig appConfig, List<PlcAbsractFactory> plcFactories, List<RfidAbsractFactory> rfidFactories, IMesProductPlanService mesProductPlanService, IBasePalletInfoService basePalletInfoService, IBaseRealTaskService baseRealTaskService, IServiceProvider serviceProvider, ConfigInfoBusiness configInfoBusiness) : base(logger, appConfig, plcFactories, rfidFactories, serviceProvider)
        {

            sqlSugarClient = _sqlSugarClient;
            _wmsOutStockService = wmsOutStockService;
            _mesProductPlanService = mesProductPlanService;
            _basePalletInfoService = basePalletInfoService;
            _baseRealTaskService = baseRealTaskService;
            _mesPrdBarCodeService = serviceProvider.GetRequiredService<IMesPrdBarCodeService>();
            _configInfoBusiness = configInfoBusiness;

           // TestTran();
        }

      

       
        public void TestTran()
        {
          
            //try
            //{
            //    sqlSugarClient.AsTenant().BeginTran();
            //    BaseRealTask localPlan = _baseRealTaskService.GetExeTask();
            //    localPlan.Status = 2;
            //    sqlSugarClient.AsTenant().GetConnection("local").Updateable(localPlan).ExecuteCommand();
            //    GenerateBarcode(40);
            //    // throw new Exception("事务测试");
            //    sqlSugarClient.AsTenant().CommitTran();
            //    Console.WriteLine("提交事务");

            //}
            //catch (Exception ex)
            //{
            //    sqlSugarClient.AsTenant().RollbackTran();
            //    Console.WriteLine(ex.Message);
            //}
        }

        private async void testRFID()
        {
            //TcpClient tcpClient = new TcpClient();

            //var waitClient = tcpClient.CreateWaitingClient(new WaitingOptions()
            //{
            //    FilterFunc = response =>
            //    {
            //        return true;
            //    }
            //});
            //tcpClient.Setup(new TouchSocketConfig().SetRemoteIPHost($"192.168.2.51:3000"));
            //tcpClient.Connect();
            // byte[] reciveBuffer = await waitClient.SendThenReturnAsync(pMessagePack.m_pData, timeout);
            string aaa = await ReadEpcStrByRfidKeyAsync("secondFloorPallet");
            //     ReadEpcStrByRfidKey("test", out string epcStr);
            Console.WriteLine(aaa);
            //RefreshMessage($"投料校验RFID标签读取成功，标签信息：{epcStr}");

        }

        #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 aaa = _mesProductPlanService.GetMesProductPlans();
                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
                {

                    RefreshMesProdList();
                    var plc = base.GetPlcByKey("plc");

                    if (plc == null || !plc.IsConnected)
                    {
                        //throw new ArgumentException($"PLC连接信息为空");
                        RefreshMessage($"PLC连接信息为空");
                        Thread.Sleep(3000);
                        continue;
                    }

                    if (!plc.readBoolByAddress(GetPlcAddressByConfigKey("设备叫料")))
                    {
                     //   RefreshMessage("3F等待设备叫料信号触发......");
                        Thread.Sleep(5000);
                        continue;
                    }

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


                   

                    WcsTaskManual wcsTaskManual =  sqlSugarClient.AsTenant().GetConnection("mes").Queryable<WcsTaskManual>().Where(x=>x.TaskType==999).First();
                    WcsTask wcsTask =  sqlSugarClient.AsTenant().GetConnection("mes").Queryable<WcsTask>().Where(x=>x.NextPointId==9 && x.TaskType==41).First();
                    if(wcsTaskManual!=null || wcsTask != null)
                    {
                        RefreshMessage("3F已经叫料，请等待AGV送料......");

                        Thread.Sleep(5000);
                        continue;
                    }

                 
                    //BaseRealTask task = _baseRealTaskService.GetExeTask();
                    //if (task != null)
                    //{
                    //    RefreshMessage("已经叫料，请等待AGV送料......");

                    //    Thread.Sleep(5000);
                    //    continue;
                    //}



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

                  //  RefreshProdPlanExecEvent?.Invoke(prodPlan);

                    // string palletCode = GetPalletInfoByTask();

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

                    //查询下发agv的主键
                    WmsRawOutstock wmsRaw = _wmsOutStockService.GetProdPlanByPlanCode(productPlanDto.PlanCode);
                    if (wmsRaw == null && debugConfig.ProductMode != "1")   //不是手动模式，没有申请单
                    {
                        RefreshMessage("自动模式，查询不到该计划出库申请单，请先创建...");
                        Thread.Sleep(5000);
                        continue;
                    }

                    //进行叫料
                    if (ApplyDeliveryHandle(wmsRaw))
                    {
                        RefreshMessage("3F根据计划自动申请叫料成功");
                        #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;
            var plc = base.GetPlcByKey("plc");
            while (true)
            {
                //mes计划
              //  MesProductPlan prodPlan = null;
                //本地叫料任务
              //  BaseRealTask localPlan = null;
                try
                {

                //    localPlan = _baseRealTaskService.GetExeTask();

                    //if (localPlan == null)
                    //{
                    //    RefreshMessage("本地未获取到叫料任务......");
                    //    Thread.Sleep(3000);
                    //    continue;
                    //}


                    if (plc == null)
                    {
                       // RefreshMessage("读取物料到位信号,PLC连接信息为空......");
                        Thread.Sleep(3000);
                        continue;
                    }
                    //判断请求翻转信号
                    if (!plc.readBoolByAddress(GetPlcAddressByConfigKey("物料到位")))
                    {
                       // RefreshMessage("等待物料到位信号触发......");
                        Thread.Sleep(3000);
                        continue;
                    }

                   
                    WcsTask wcsTask = sqlSugarClient.AsTenant().GetConnection("mes").Queryable<WcsTask>().Where(x => x.NextPointId == 9 && (x.TaskType == 41 || x.TaskType == 42)).First();
                    if (wcsTask == null)
                    {
                        RefreshMessage("3F到位信号触发,但是未检测到有原材料出库任务......");
                        Thread.Sleep(5000);
                        continue;
                    }
                    

                    // TODO  投料时间校验
                    bool timeCheck = JudgeProductTime();
                    if (!timeCheck)
                    {
                        Thread.Sleep(1000 * 15);
                        continue;
                    }
                    RefreshMessage($"3F投料时间校验通过");

                    if (debugConfig.SystemStatus == "0")
                    {
                        RefreshMessage($"设备处于停机中，禁止翻转");
                        Thread.Sleep(1000 * 5);
                        continue;
                    }

                    // 调用调度接口，判断AGV小车是否已经离开到位，否则不允许投料
                    bool positionCheck = JudgeAgvPosition();
                    if (!positionCheck)
                    {
                        RefreshMessage($"3F收到物料到位信号,但是AGV还未离开,禁止投料");
                        Thread.Sleep(1000 * 5);
                        continue;
                    }

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

                    if (productPlanDto == null)
                    {
                        RefreshMessage($"3F收到物料到位信号,未获取到正在执行的生产计划,请开始执行计划");

                        Thread.Sleep(5000);
                        continue;
                    }

                    #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

                    if (productPlanDto != null)
                    {
                        // 下发翻转信号
                        plc.writeBoolByAddress(GetPlcAddressByConfigKey("设备投料"), true);
                        Thread.Sleep(300);
                        plc.writeBoolByAddress(GetPlcAddressByConfigKey("设备投料"), false);


                        sqlSugarClient.AsTenant().BeginTran();                 
                        //1.完成本地计划
                        //if (localPlan != null)
                        //{
                        //    List<BaseRealTask> localList = _baseRealTaskService.Query(x => x.Status != 2);
                        //    foreach (var item in localList)
                        //    {
                        //        item.Status = 2;
                        //        item.UpdateTime = DateTime.Now;
                        //    }
                        //    sqlSugarClient.AsTenant().GetConnection("local").Updateable(localList).ExecuteCommand();
        
                        //}
                        //2.生成小包条码
                        GenerateBarcode(40);

                        //BaseConfigInfo configInfo = _configInfoBusiness.GetConfigInfos().Where(x => x.ConfigKey == "系统运行时长").FirstOrDefault();
                        //configInfo.ConfigValue = "0";
                        //sqlSugarClient.AsTenant().GetConnection("local").Updateable(configInfo).ExecuteCommand();
                        //_configInfoBusiness.RefreshConfigInfo();

                        sqlSugarClient.AsTenant().CommitTran();
                        //3.清空投料系统运行时间
                        BaseConfigInfo configInfo = _configInfoBusiness.GetConfigInfos().Where(x => x.ConfigKey == "系统运行时长").FirstOrDefault();
                        configInfo.ConfigValue = "0";
                        _configInfoBusiness.UpdateConfigInfo(configInfo);
                    }

                }
                catch (Exception e)
                {
                    sqlSugarClient.AsTenant().RollbackTran();
                    _logger.LogInformation($"投料流程异常:{e.Message}");
                    Thread.Sleep(5000);

                  
                }

                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>
        /// 申请叫料，等待调度接口
        /// </summary>
        /// <param name="materialCode"></param>
        /// <param name="result"></param>
        /// <exception cref="InvalidOperationException"></exception>
        public bool ApplyDeliveryHandle(WmsRawOutstock wmsRaw)
        {
            bool result = false;
            try
            {
                //判断当前模式，手动需从出库队列叫料，如果没有出库队列，弹框提醒；自动模式，库位参数为Null
                string targetLocationCode = null;
                if(debugConfig.ProductMode== "1")
                {
                    // 手动模式出库队列
                    WmsRawPreferredOut? PreferredOut = sqlSugarClient.AsTenant().GetConnection("mes").Queryable<WmsRawPreferredOut>().OrderBy(x=>x.RawPreferredOutId).First();
                    if (PreferredOut == null)
                    {
                        RefreshMessage("当前投料手动模式,无出库队列,请在投料管理添加出库队列，或修改为自动投料模式");
                       // MsgHandlerEvent?.Invoke("当前投料手动模式,无出库队列,请在投料管理添加出库队列，或修改为自动投料模式", 2, 4);
                        return false;
                    }
                    else
                    {
                        targetLocationCode = PreferredOut.LocationCode;
                    }
                }
                //if (debugConfig.ProductMode == "1")
                //{  //标记手工叫料
                //    taskId = "-1";
                //}
                var content = new
                {
                    rawOutstockId = wmsRaw==null?"-1": wmsRaw.rawOutstockId.ToString(),
                    locationCode = targetLocationCode,
                };
               

                string message = JsonConvert.SerializeObject(content);
                //询问小车是否离开接口 0已经离开
                string httpResult = HttpHelper.SendPostMessage("172.16.12.100", 5001, "wcs/RecieveRcs/callMaterial", message, "application/Json");


                var reponseMessage = JsonConvert.DeserializeObject<ReponseMessage>(httpResult);
                if (reponseMessage != null && reponseMessage.code == "0")
                {
                    RefreshMessage("Agv叫料成功");
                    result = true;
                }

            }
            catch (Exception ex)
            {
                RefreshMessage($"申请叫料处理异常:{ex.Message}");
                result = false;
            }

            return result;
        }



        /// <summary>
        /// 投料后产生小包条码存储到数据库等待绑定
        /// </summary>
        private void GenerateBarcode(int amount)
        {
            // JY2408271423CF015
            List<MesPrdBarcodeInfo> list = new List<MesPrdBarcodeInfo>();
            string baseDate = DateTime.Now.ToString("yyyyMMddHHmm"); // 当前日期和时间到分钟
            baseDate = "JY" + baseDate.Substring(2);
            for (int i = 1; i <= amount; i++)
            {
                MesPrdBarcodeInfo record = new MesPrdBarcodeInfo();
                record = new MesPrdBarcodeInfo();
                record.PrdBarcodeInfo = baseDate + "CF" + i.ToString("D3"); // 将序号格式化为三位数
                record.PrintFlag = "0";
                record.CreatTime = DateTime.Now;
                record.BindStatus = 0;
                list.Add(record);
            }
            sqlSugarClient.AsTenant().GetConnection("mes").Insertable(list).ExecuteCommand();
        }


        /// <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("投料时间间隔"));   // 单位分钟
                int systemRunTime = int.Parse(GetPlcAddressByConfigKey("系统运行时长"));   // 单位分钟

                if (systemRunTime > IntervalMin)
                {
                    return true;
                }
                else
                {
                    RefreshMessage($"收到物料到位请求投料信号,投料间隔：{IntervalMin}min,距上次投料:{systemRunTime}min,禁止投料");
                    return false;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"距离上次投料时间合格判断异常JudgeProductTime：{ex.Message}");
                return false;
            }
        }

        private bool JudgeAgvPosition()
        {
            bool result = false;
            //询问小车是否离开接口 0已经离开
            string httpResult = HttpHelper.SendPostMessage("172.16.12.100", 5001, "wcs/RecieveRcs/AgvTaskComplete", "", "application/Json");

            var reponseMessage = JsonConvert.DeserializeObject<ReponseMessage>(httpResult);
            if (reponseMessage != null && reponseMessage.code == "0")
            {
                RefreshMessage("Agv已经离开...");
                result = true;
            }
            return result;
        }
    }
}
