using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using Siger.ApiQMS.Result;
using Siger.Middlelayer.Common;
using Siger.Middlelayer.Common.Helpers;
using Siger.Middlelayer.Common.ModuleEnum;
using Siger.Middlelayer.Log;
using Siger.Middlelayer.QmsRepository.Entities;
using Siger.Middlelayer.QmsRepository.Repositories.Interface;
using Siger.Middlelayer.Repository;
using Siger.Middlelayer.Repository.Repositories.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Siger.ApiQMS.Utility
{
    public class AbnormalCheckHelper
    {
        private readonly ISigerDict _sigerDict;
        private readonly IAbnormalRuleRepository _abnormalRuleRepository;
        private readonly ISigerProjectUserRepository _projectUserRepository;
        private readonly ICheckSnTraceInspectionRepository _traceInspectionRepository;
        private readonly ICheckSnTraceDetailRepository _traceDetailRepository;
        private readonly IInspectStandardRepository _inspectStandard;
        private readonly IUnitOfWork _unitOfWork;
        private readonly int _projectId;
        private readonly int _companyId;
        private readonly int _userId;
        public AbnormalCheckHelper(IUnitOfWork unitOfWork, IAbnormalRuleRepository abnormalRuleRepository, ISigerProjectUserRepository projectUserRepository,
            ICheckSnTraceDetailRepository traceDetailRepository, IInspectStandardRepository inspectStandard, ICheckSnTraceInspectionRepository traceInspectionRepository,
            ISigerDict sigerDict, int companyId, int projectId, int userId)
        {
            _unitOfWork = unitOfWork;
            _abnormalRuleRepository = abnormalRuleRepository;
            _projectUserRepository = projectUserRepository;
            _traceDetailRepository = traceDetailRepository;
            _inspectStandard = inspectStandard;
            _projectId = projectId;
            _sigerDict = sigerDict;
            _userId = userId;
            _companyId = companyId;

            _traceInspectionRepository = traceInspectionRepository;
        }


        /// <summary>
        /// 检验数据差异
        /// </summary>
        /// <param name="num"></param>
        /// <param name="traceid"></param>
        /// <param name="sectionid"></param>
        /// <param name="machineid"></param>
        /// <param name="triggerAndon"></param>
        /// <param name="typeCode"></param>
        public void AbnomalRuleCheck(int num, string traceid, int sectionid, int machineid, bool triggerAndon, int typeCode)
        {
            if (num <= 0)
            {
                return;
            }
            //获取判异规则，按照key排序，对应python中的顺序
            var rules = _abnormalRuleRepository.GetList(t => t.projectid == _projectId &&
                t.status == (int)RowState.Valid).OrderBy(t => t.key).ToList();
            //数分要求(算法中需要额外的数据)，最大数量+1
            var maxRule = rules.Max(t => t.k_value) + 1;
            var maxRule2 = maxRule * num;//需要的总数量
            //获取检验数据，按照CreateTime,NumberIndex,ID排序，得到实际操作顺序
            var traceData = _traceDetailRepository.GetList(t => t.projectid == _projectId &&
                t.status == (int)RowState.Valid && t.TraceID == traceid)
                .OrderBy(t => t.CreateTime).OrderBy(t => t.NumberIndex).OrderBy(t => t.ID).ToList();

            var items = _inspectStandard.GetList(t => t.projectid == _projectId &&
                t.status == (int)RowState.Valid && traceData.Select(q => q.ItemID).Contains(t.id) &&
                t.value_type == (int)ValueTypeStatus.V).ToList();

            var valueItemIds = items.Select(t => t.id).ToList();

            var ruleKeyValues = rules.Select(t => t.k_value).ToList();

            var ruleDatas = new List<RuleTraceData>();
            foreach (var itemid in valueItemIds)
            {
                var item = items.FirstOrDefault(t => t.id == itemid);
                if (item == null || !item.ucl.HasValue || !item.lcl.HasValue)
                {
                    continue;
                }

                var data = traceData.Where(t => t.ItemID == itemid && t.Value.HasValue).ToList();
                if (!data.Any())
                {
                    continue;
                }

                //python判异代码需要的输入参数
                var model = new RuleTraceData
                {
                    itemID = itemid,
                    UCL = item.ucl.Value,
                    LCL = item.lcl.Value,
                    traceID = traceid,
                    traceDetailID = data.Select(q => q.ID).ToList()
                };
                //获取当前数据，做为python判异代码需要的数据
                var checkRuleData = data.Select(t => t.Value.Value).ToList();

                if (checkRuleData.Count < maxRule2)
                {
                    var checkTotalData = _traceDetailRepository.GetHistoryDts(maxRule2, traceid, typeCode, _projectId, itemid, data.Min(q => q.CreateTime));
                    checkTotalData.Reverse();
                    //数据不够从时间最近的检验中补充数据
                    model.value.AddRange(checkTotalData.TakeLast(maxRule2 - checkRuleData.Count).ToList());
                }
                model.value.AddRange(checkRuleData);

                ruleDatas.Add(model);
            }
            var spcHelper = new SpcAnalysisUtility();
            var updateEntities = new List<SpcCheckResData>();
            if (ruleDatas.Count > 0)
            {
                Parallel.ForEach(ruleDatas, new ParallelOptions { MaxDegreeOfParallelism = ruleDatas.Count },
                    t => { updateEntities.Add(AbnormalCheckPython(t, ruleKeyValues, spcHelper, num)); });
                Task.WaitAll();
            }

            var triggerRules = rules.Where(t => t.trigger_andon == 1);
            var abnormalStr = string.Empty;
            foreach (var updateEntity in updateEntities)
            {
                var triggerCheck = false;
                if (updateEntity == null)
                {
                    continue;
                }
                var traceDetails = _traceDetailRepository.GetList(t => t.projectid == _projectId && t.status == (int)RowState.Valid &&
                    updateEntity.ids.Contains(t.ID)).OrderBy(q => q.CreateTime).OrderBy(q => q.NumberIndex).OrderBy(q => q.ID).AsNoTracking().ToList();
                if (!traceDetails.Any())
                {
                    continue;
                }
                if (!string.IsNullOrEmpty(updateEntity.abnomal_status) &&
                        triggerRules.FirstOrDefault(t => updateEntity.abnomal_status.Contains(t.key.ToString())) != null)
                {
                    triggerCheck = true;
                }
                foreach (var traceDetail in traceDetails)
                {
                    var item = items.FirstOrDefault(t => t.id == traceDetail.ItemID);
                    if (item != null && item.trigger_andon == 1 && triggerCheck)
                    {
                        abnormalStr = GetAbnormalStr(rules, updateEntity.abnomal_status);
                    }
                    var updateModel = _traceDetailRepository.Get(q => q.ID == traceDetail.ID);
                    if (updateModel != null)
                    {
                        updateModel.abnomal_status = updateEntity.abnomal_status;
                        _traceDetailRepository.Update(updateModel);
                    }
                }
            }
            if (_unitOfWork.Commit() > 0 && triggerAndon && !string.IsNullOrEmpty(abnormalStr))
            {
                var workOrder = TriggerAndon(sectionid, machineid, abnormalStr);
                UpdateTraceWorkOrder(traceid, workOrder);//回填安灯工单号到检验表
            }
        }

        private void UpdateTraceWorkOrder(string traceid, string workOrder)
        {
            if (string.IsNullOrEmpty(workOrder))
            {
                return;
            }
            var traceDataInfo = _traceInspectionRepository.Get(q => q.trace_id == traceid && q.projectid == _projectId && q.status == (int)RowState.Valid);
            if (traceDataInfo == null)
            {
                return;
            }

            traceDataInfo.workorder = workOrder;
            _traceInspectionRepository.Update(traceDataInfo);
            if (_unitOfWork.Commit() <= 0)
            {
                return;
            }
        }

        /// <summary>
        /// 获取判异说明
        /// </summary>
        /// <param name="rules"></param>
        /// <param name="abnomalStatus"></param>
        /// <returns></returns>
        private string GetAbnormalStr(List<siger_qms_abnormal_rule> rules, string abnomalStatus)
        {
            string abnormalStr = string.Empty;
            var rule = rules.Where(t => abnomalStatus.Contains(t.key.ToString())).ToList();
            if (rule.Any())
            {
                for (var x = 0; x < rule.Count; x++)
                {
                    abnormalStr += $"({x + 1}).{rule[x].rule};";
                }
            }
            return abnormalStr;
        }

        private SpcCheckResData AbnormalCheckPython(RuleTraceData ruleData, List<int> ruleKeyValues, SpcAnalysisUtility spcHelper, int num)
        {
            var checkDataList = new List<double>();
            //根据检验数量取平均值            
            ruleData.value.Reverse();//从后往前取数，保证当数据不全时最新数据不被分割
            for (int key = 0; key < ruleData.value.Count / num; key++)
            {
                checkDataList.Add(ruleData.value.Skip(num * key).Take(num).Average());
            }
            if (ruleData.value.Count % num > 0)
            {
                ruleData.value.Reverse();
                checkDataList.Add(ruleData.value.Take(ruleData.value.Count % num).Average());
            }
            checkDataList.Reverse();//转回正常操作顺序            
            ruleData.value = checkDataList;

            var result = spcHelper.CheckDataRule(ruleData.itemID, ruleData.UCL, ruleData.LCL, ruleData.traceID,
            ruleData.value, ruleKeyValues);

            if (result == null)
            {
                return null;
            }
            var abnormalRules = new List<int>();
            if (result.rule1.HasValue)
            {
                abnormalRules.Add(1);
            }
            if (result.rule2.HasValue)
            {
                abnormalRules.Add(2);
            }
            if (result.rule3.HasValue)
            {
                abnormalRules.Add(3);
            }
            if (result.rule4.HasValue)
            {
                abnormalRules.Add(4);
            }
            if (result.rule5.HasValue)
            {
                abnormalRules.Add(5);
            }
            if (result.rule6.HasValue)
            {
                abnormalRules.Add(6);
            }
            if (result.rule7.HasValue)
            {
                abnormalRules.Add(7);
            }
            if (result.rule8.HasValue)
            {
                abnormalRules.Add(8);
            }
            if (abnormalRules.Any())
            {
                var traceDetail = new SpcCheckResData
                {
                    ids = ruleData.traceDetailID,
                    ItemID = ruleData.itemID,
                    abnomal_status = string.Join(',', abnormalRules.Distinct().ToList()),
                };
                return traceDetail;
            }
            return null;
        }

        /// <summary>
        /// 触发安灯
        /// </summary>
        private string TriggerAndon(int sectionid, int machineid, string abnormalStr)
        {
            var exceptionDict = _sigerDict.Get(t => t.projectId == _projectId && t.status == (int)RowState.Valid &&
                t.cat == AccDictCost.QmsCheckNgAndon);
            if (exceptionDict == null || string.IsNullOrWhiteSpace(exceptionDict.dval))
            {
                return "";
            }
            var dicts = exceptionDict.dval.Split(';', StringSplitOptions.RemoveEmptyEntries).ToList();
            if (!dicts.Any())
            {
                return "";
            }
            //检查异常类型-异常类型从字典中获取，字典中的数据必须按照异常等级从低到高用分号隔开
            //不限制配置的异常类型数量，因为异常类型名称可能重复，所以要配置多个等级异常类型
            var expectionModel = _inspectStandard.GetAndonExceptionType(dicts[0], 0, _projectId);
            if (expectionModel == null)
            {
                return "";
            }
            var parentid = expectionModel.id;
            foreach (var dict in dicts)
            {
                if (dict == dicts[0])
                {
                    continue;
                }
                expectionModel = _inspectStandard.GetAndonExceptionType(dict, parentid, _projectId);
                if (expectionModel == null)
                {
                    return "";
                }
                parentid = expectionModel.id;
            }
            var process = expectionModel.process_code ?? "";
            var order = _inspectStandard.WorkOrderGenerator(_projectId);

            var status = (int)AndonState.WaitHandle;
            var now = UnixTimeHelper.GetNow();
            var model = new siger_andon_info
            {
                projectid = _projectId,
                status = status,
                creator = _userId,
                station = sectionid,
                process_code = process,
                is_shutdown = 0,
                machine = machineid,
                expection_type = expectionModel.id,
                cruent_level = 1,
                remark = abnormalStr,
                work_order = order ?? "",
                create_time = now
            };
            if (expectionModel.is_passprocess == 1)
            {
                status = (int)AndonState.Complete;
                model.status = status;
                model.handle_time = now;
                //init 签到时间和签到人 解除时间 解除人
                model.handler = _userId;
                model.complete_time = now;
            }
            if (_inspectStandard.InsertAndonInfo(model))
            {
                var user = _projectUserRepository.Get(t => t.projectid == _projectId && t.status == (int)RowState.Valid &&
                    t.mid == _userId);
                var entity = new siger_andon_info_detail
                {
                    andon_id = model.id,
                    createtime = now,
                    projectid = _projectId,
                    status = status,
                    user = user?.name ?? "",
                    remark = abnormalStr
                };
                _inspectStandard.InsertAndonDetailInfo(entity);
                return order;
            }
            else
            {
                return "";
            }
        }
    }
}
