using DataLinkMesWork1;
using MesServerWork;
using MisDataFunDll;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;

namespace MesServerFunctions
{
    partial class FunctionMES
    {
        // OP431 手动静置出库固定配置。
        private const string ManualStaticOutOpName = "OP431";
        private const string ManualStaticPadTopic = "OP431PAD";
        private const decimal DefaultManualStaticMinutes = 30m;
        private const string ManualStaticLocation = "777777";
        private const string ManualStaticOperator = "PAD";

        // 记录 PAD 确认时使用的最小静置分钟数，保存质量数据时沿用同一标准。
        private static readonly object ManualStaticMinutesLocker = new object();
        private static readonly Dictionary<string, decimal> ManualStaticMinutesByEngine = new Dictionary<string, decimal>();

        /// <summary>
        /// OP431 当前产品基础信息。
        /// </summary>
        private class ManualStaticOutProduct
        {
            public string EngineID { get; set; }
            public string OrderForm { get; set; }
            public string EngineType { get; set; }
            public int EngineTypeID { get; set; }
        }

        /// <summary>
        /// OP431 静置质量数据计算结果。
        /// </summary>
        private class ManualStaticOutData
        {
            public string SourceOpName { get; set; }
            public DateTime StartTime { get; set; }
            public DateTime FinishTime { get; set; }
            public decimal Minutes { get; set; }
            public int QualityMask { get; set; }
        }

        /// <summary>
        /// 查询产品订单、型号和机型代码。
        /// </summary>
        private bool TryGetManualStaticOutProduct(string engineID, out ManualStaticOutProduct product, out string message)
        {
            product = null;
            message = "";

            if (string.IsNullOrWhiteSpace(engineID))
            {
                message = "产品码为空";
                return false;
            }

            // PAD 可能扫描箱体码，最终产品编号以存储过程返回的产品编号为准。
            if (!TrySelectManualStaticPdcProduct(engineID, out string productCode, out string orderForm, out string engineType, out int engineTypeID, out message))
            {
                return false;
            }

            product = new ManualStaticOutProduct
            {
                EngineID = productCode,
                OrderForm = orderForm,
                EngineType = engineType,
                EngineTypeID = engineTypeID
            };
            return true;
        }

        /// <summary>
        /// 查询 PDC 产品执行信息，兼容产品码和箱体码输入。
        /// </summary>
        private bool TrySelectManualStaticPdcProduct(string scanCode, out string productCode, out string orderForm, out string engineType, out int engineTypeID, out string message)
        {
            productCode = "";
            orderForm = "";
            engineType = "";
            engineTypeID = 0;
            message = "";

            try
            {
                SqlParameter[] sqlParameters = new SqlParameter[1];
                sqlParameters[0] = new SqlParameter("@发动机号", scanCode);
                DataAccess.ExecuteStoredProcedure("测量数据工位_监控系统_MOBY_查询_PDC", ref sqlParameters, out DataTable dt, out string errorMessage);
                if (!string.IsNullOrWhiteSpace(errorMessage))
                {
                    message = "查询产品执行信息失败：" + errorMessage;
                    return false;
                }

                if (dt == null || dt.Rows.Count == 0)
                {
                    message = "未查询到产品执行信息：" + scanCode;
                    return false;
                }

                DataRow row = dt.Rows[0];
                productCode = dt.Columns.Contains("产品编号") ? Convert.ToString(row["产品编号"]) : Convert.ToString(row["发动机号"]);
                orderForm = Convert.ToString(row["订单号"]);
                engineType = Convert.ToString(row["产品型号"]);
                engineTypeID = Convert.ToInt32(row["机型代码"]);

                if (string.IsNullOrWhiteSpace(productCode) || engineTypeID <= 0 || string.IsNullOrWhiteSpace(engineType))
                {
                    message = "产品执行信息不完整：" + scanCode;
                    return false;
                }

                productCode = productCode.Trim();
                return true;
            }
            catch (Exception err)
            {
                message = "查询产品执行信息失败：" + err.Message;
                return false;
            }
        }

        /// <summary>
        /// 查询 OP420 记录的线下手动静置开始时间。
        /// </summary>
        private bool TryGetManualStaticStartTime(string engineID, out string sourceOpName, out DateTime startTime, out string message)
        {
            sourceOpName = "";
            startTime = DateTime.MinValue;
            message = "";

            try
            {
                // OP431 手动出库以 OP420 记录的线下静置开始时间为准。
                string procedureName = "线下手动静置_记录_查询";
                SqlParameter[] sqlParameters = new SqlParameter[1];
                sqlParameters[0] = new SqlParameter("@产品编号", engineID);

                DataAccess.ExecuteStoredProcedure(procedureName, ref sqlParameters, out DataTable dt, out string errorMessage);
                if (!string.IsNullOrWhiteSpace(errorMessage))
                {
                    message = "查询线下手动静置开始时间失败：" + errorMessage;
                    return false;
                }

                if (dt == null || dt.Rows.Count == 0)
                {
                    message = "未查询到线下手动静置开始记录：" + engineID;
                    return false;
                }

                sourceOpName = "OP420";
                startTime = Convert.ToDateTime(dt.Rows[0]["静置开始时间"]);
                return true;
            }
            catch (Exception err)
            {
                message = "查询线下手动静置开始时间失败：" + err.Message;
                return false;
            }
        }

        /// <summary>
        /// OP431 保存时回写线下手动静置结束时间和静置时长。
        /// </summary>
        private bool FinishManualStaticRecord(string engineID, DateTime finishTime, decimal minutes, out string message)
        {
            message = "";
            SqlParameter[] sqlParameters = new SqlParameter[]
            {
                new SqlParameter("@产品编号", engineID),
                new SqlParameter("@静置结束时间", finishTime),
                new SqlParameter("@静置时长", minutes)
            };

            DataAccess.ExecuteStoredProcedure("线下手动静置_记录_结束", ref sqlParameters, out string errorMessage);
            if (!string.IsNullOrWhiteSpace(errorMessage))
            {
                message = "回写线下手动静置结束时间失败：" + errorMessage;
                return false;
            }

            return true;
        }

        /// <summary>
        /// 生成 OP431 保存时需要的静置时间和质量标识。
        /// </summary>
        private bool TryBuildManualStaticOutData(string engineID, int qualityMask, out ManualStaticOutData staticOutData, out string message)
        {
            staticOutData = null;
            message = "";

            if (!TryGetManualStaticStartTime(engineID, out string sourceOpName, out DateTime startTime, out message))
            {
                return false;
            }

            // PLC 允许保存到达时的服务器时间作为静置完成时间。
            DateTime finishTime = DateTime.Now;
            decimal minutes = Math.Round(Convert.ToDecimal((finishTime - startTime).TotalMinutes), 2);
            staticOutData = new ManualStaticOutData
            {
                SourceOpName = sourceOpName,
                StartTime = startTime,
                FinishTime = finishTime,
                Minutes = minutes,
                QualityMask = NormalizeManualStaticQualityMask(qualityMask)
            };
            return true;
        }

        /// <summary>
        /// 标准化质量标识：1 为合格，其余均按不合格处理。
        /// </summary>
        private int NormalizeManualStaticQualityMask(int qualityMask)
        {
            return qualityMask == 1 ? 1 : 2;
        }

        /// <summary>
        /// 判断静置时长是否达到 PAD 配置的最小分钟数。
        /// </summary>
        private bool IsManualStaticTimeEnough(ManualStaticOutData staticOutData, decimal minStaticMinutes, out string message)
        {
            message = "";
            if (staticOutData.Minutes < minStaticMinutes)
            {
                message = "静置时间不足 " + minStaticMinutes.ToString("0.##") + " 分钟，当前已静置 " + staticOutData.Minutes + " 分钟";
                return false;
            }
            return true;
        }

        /// <summary>
        /// 直接写入 OP431 四项静置质量数据。
        /// </summary>
        private bool WriteManualStaticOutQualityData(string opName, string engineID, ManualStaticOutData data, out string message)
        {
            message = "";
            int idTagCf = 0;
            if (!TryGetManualStaticMinutesForProduct(engineID, out decimal rememberedMinStaticMinutes))
            {
                message = "未找到PAD确认的最小静置标准，请在PAD重新确认上线后再保存：" + engineID;
                return false;
            }

            // 先写质量索引，取返回的 Id_tagCF 作为明细关联键。
            string minStaticMinutes = rememberedMinStaticMinutes.ToString("0.##");

            // OP431 的静置数据由 Function 直接落库，不再通过 OnlineCheckData 做 HTTP 自调用。
            SqlParameter[] indexParams = new SqlParameter[6];
            indexParams[0] = new SqlParameter("@EngineID", engineID);
            indexParams[1] = new SqlParameter("@工位号", opName);
            indexParams[2] = new SqlParameter("@操作者工号", ManualStaticOperator);
            indexParams[3] = new SqlParameter("@扫码时间", data.FinishTime);
            indexParams[4] = new SqlParameter("@合格标志", data.QualityMask);
            indexParams[5] = new SqlParameter("@Id_tagCF", idTagCf);
            indexParams[5].Direction = ParameterDirection.Output;

            DataAccess.ExecuteStoredProcedure("测量数据合格索引_增加_测试设备", ref indexParams, out string errorMessage);
            if (!string.IsNullOrWhiteSpace(errorMessage))
            {
                message = "写入静置质量索引失败：" + errorMessage;
                return false;
            }

            idTagCf = Convert.ToInt32(indexParams[5].Value.ToString());
            if (idTagCf <= 0)
            {
                message = "写入静置质量索引失败：未生成 Id_tagCF";
                return false;
            }

            // 四条质量明细必须与 MOM 数据收集组配置的 TagID 保持一致。
            object[,] detailRows = new object[,]
            {
                { "OP431_M0001_001", "开始静置时间", "min", "1", "60", minStaticMinutes, data.StartTime.ToString("yyyy-MM-dd HH:mm:ss"), data.QualityMask },
                { "OP431_M0001_002", "静置完成时间", "min", "1", "60", minStaticMinutes, data.FinishTime.ToString("yyyy-MM-dd HH:mm:ss"), data.QualityMask },
                { "OP431_M0001_003", "静置时间差", "min", "1", "60", minStaticMinutes, data.Minutes.ToString("0.##"), data.QualityMask },
                { "OP431_M0001_004", "静置库位号", "静置库位", "-1", "-1", "-1", ManualStaticLocation, data.QualityMask }
            };

            for (int i = 0; i < detailRows.GetLength(0); i++)
            {
                SqlParameter[] detailParams = new SqlParameter[]
                {
                    new SqlParameter("@Id_tagCF", idTagCf),
                    new SqlParameter("@EngineID", engineID),
                    new SqlParameter("@TagID", detailRows[i, 0]),
                    new SqlParameter("@TagValue", detailRows[i, 6]),
                    new SqlParameter("@IsGoodBad", detailRows[i, 7]),
                    new SqlParameter("@工位号", opName),
                    new SqlParameter("@理论值", detailRows[i, 3]),
                    new SqlParameter("@上限值", detailRows[i, 4]),
                    new SqlParameter("@下限值", detailRows[i, 5]),
                    new SqlParameter("@测量项目", detailRows[i, 1]),
                    new SqlParameter("@测量单位", detailRows[i, 2])
                };

                DataAccess.ExecuteStoredProcedure("测量数据合格_增加_测试设备", ref detailParams, out errorMessage);
                if (!string.IsNullOrWhiteSpace(errorMessage))
                {
                    message = "写入静置质量明细失败：" + errorMessage;
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// 确认上线前校验托盘到位和 PLC 申请上线状态。
        /// </summary>
        private bool CheckManualStaticPadPrecondition(string opName, out string message)
        {
            message = "";

            MIS_BASE.Read_WorkStart(opName, out bool workStart);
            if (!workStart)
            {
                message = "当前无托盘到位信号，不允许扫码确认";
                return false;
            }

            MIS_BASE.Read_PartLoad(opName, out bool partLoad);
            if (!partLoad)
            {
                message = "PLC尚未申请上线，不允许扫码确认";
                return false;
            }

            return true;
        }

        /// <summary>
        /// PAD 确认上线：校验静置条件，通过后写入 PLC 产品区。
        /// </summary>
        private bool ConfirmManualStaticOutProduct(string opName, string engineID, decimal minStaticMinutes, out string message)
        {
            message = "";

            // PAD 确认上线时必须重新校验托盘和 PLC 申请状态，不能复用查询时的旧结果。
            if (!CheckManualStaticPadPrecondition(opName, out message))
            {
                return false;
            }

            if (!TryGetManualStaticOutProduct(engineID, out ManualStaticOutProduct product, out message))
            {
                return false;
            }

            if (!TryBuildManualStaticOutData(product.EngineID, 1, out ManualStaticOutData staticOutData, out message))
            {
                return false;
            }

            // 二次校验静置时间，最小分钟数来自 PAD 当前配置，默认 30 分钟。
            if (!IsManualStaticTimeEnough(staticOutData, minStaticMinutes, out message))
            {
                return false;
            }

            MIS_BASE.Read_PalletCode(opName, out int palletCode);
            MisDataFun.OpName_MIS_MOBY_Insert(opName, product.EngineTypeID, product.EngineType, product.EngineID, palletCode, product.OrderForm, 0, 0, "");

            // 写入 MES->PLC 产品区，PLC 收到 EngineTypeGetOver_MES 后再回传 44 做进站校验。
            MIS_BASE.Write_EngineTypeID_MES(opName, product.EngineTypeID);
            MIS_BASE.Write_EngineID_MES(opName, product.EngineID);
            MIS_BASE.Write_EngineType_MES(opName, product.EngineType);
            MIS_BASE.Write_OrderForm_MES(opName, product.OrderForm);
            MIS_BASE.Write_EngineTypeGetOver_MES(opName, true);
            RememberManualStaticMinutesForProduct(product.EngineID, minStaticMinutes);

            message = "PAD确认上线成功：" + product.EngineID;
            return true;
        }

        /// <summary>
        /// PAD 扫码查询：返回产品、静置时间和是否允许确认。
        /// </summary>
        private bool QueryManualStaticOutProduct(string opName, string engineID, decimal minStaticMinutes, out string resultMessage)
        {
            resultMessage = "";

            // 扫码查询只返回产品与静置信息，不写 PLC、不调用进站，也不依赖托盘到位/PLC申请上线实时消息。
            // PAD 页面可能不是一直打开，托盘到位等实时 MQTT 可能丢失，最终状态由确认上线时后端再次校验。
            if (!TryGetManualStaticOutProduct(engineID, out ManualStaticOutProduct product, out resultMessage))
            {
                return false;
            }

            if (!TryBuildManualStaticOutData(product.EngineID, 1, out ManualStaticOutData staticOutData, out resultMessage))
            {
                return false;
            }

            bool timeEnough = IsManualStaticTimeEnough(staticOutData, minStaticMinutes, out string timeMessage);
            MIS_BASE.Read_PartLoad(opName, out bool partLoad);

            // PAD 直接使用 canConfirm 控制按钮，不再自行组合判断条件。
            bool canConfirm = timeEnough && partLoad;
            string confirmMessage = canConfirm
                ? "允许确认上线"
                : !timeEnough
                    ? timeMessage
                    : "PLC尚未申请上线，不允许确认上线";

            resultMessage = string.Join("^", new string[]
            {
                product.EngineID,
                product.OrderForm,
                product.EngineType,
                product.EngineTypeID.ToString(),
                staticOutData.SourceOpName,
                staticOutData.StartTime.ToString("yyyy-MM-dd HH:mm:ss"),
                staticOutData.Minutes.ToString("0.##"),
                minStaticMinutes.ToString("0.##"),
                partLoad ? "1" : "0",
                canConfirm ? "1" : "0",
                confirmMessage
            });
            return true;
        }

        /// <summary>
        /// 将 PAD 业务结果同时发送到 PAD 主题和工位主题。
        /// </summary>
        private void SendManualStaticOutResult(string opName, string command, bool success, string message)
        {
            string value = (success ? "1" : "0") + "^" + message;
            SendManualStaticPadMqttMessage(command, opName, value);

            // 同步给工位主题，保留现有页面状态监听能力；PAD 以 OP431PAD 主题为准。
            SendMessageFun.SendInfomation(command, value, opName);
            if (!success)
            {
                SendMessage.AsyncSendMessage_Notice(opName, "【" + opName + "】" + message);
            }
        }

        /// <summary>
        /// 向 OP431 PAD 专用主题发送 MQTT 回包。
        /// </summary>
        private void SendManualStaticPadMqttMessage(string command, string opName, string value)
        {
            SendMessage.AsyncSendNotOpMessage(ManualStaticPadTopic, "MES|" + command + "|" + opName + "|" + value);
        }

        /// <summary>
        /// 记录产品确认上线时使用的最小静置标准。
        /// </summary>
        private void RememberManualStaticMinutesForProduct(string engineID, decimal minStaticMinutes)
        {
            lock (ManualStaticMinutesLocker)
            {
                ManualStaticMinutesByEngine[engineID] = minStaticMinutes;
            }
        }

        /// <summary>
        /// 获取产品确认上线时记录的最小静置标准。
        /// </summary>
        private bool TryGetManualStaticMinutesForProduct(string engineID, out decimal minStaticMinutes)
        {
            lock (ManualStaticMinutesLocker)
            {
                if (ManualStaticMinutesByEngine.ContainsKey(engineID))
                {
                    minStaticMinutes = ManualStaticMinutesByEngine[engineID];
                    return true;
                }
            }

            minStaticMinutes = DefaultManualStaticMinutes;
            return false;
        }
    }
}
