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

namespace MesServerFunctions
{
    partial class FunctionMES
    {
        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";
        private static readonly object ManualStaticMinutesLocker = new object();
        private static readonly Dictionary<string, decimal> ManualStaticMinutesByEngine = new Dictionary<string, decimal>();

        private class ManualStaticOutProduct
        {
            public string EngineID { get; set; }
            public string OrderForm { get; set; }
            public string EngineType { get; set; }
            public int EngineTypeID { get; set; }
        }

        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; }
        }

        private bool TryGetManualStaticOutProduct(string engineID, out ManualStaticOutProduct product, out string message)
        {
            product = null;
            message = "";

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

            MisDataFun.Moby_Select_PDC(engineID, out string orderForm, out string engineType, out int engineTypeID);
            if (engineTypeID <= 0 || string.IsNullOrWhiteSpace(engineType))
            {
                message = "未查询到产品执行信息：" + engineID;
                return false;
            }

            product = new ManualStaticOutProduct
            {
                EngineID = engineID.Trim(),
                OrderForm = orderForm,
                EngineType = engineType,
                EngineTypeID = engineTypeID
            };
            return true;
        }

        private bool TryGetManualStaticStartTime(string engineID, out string sourceOpName, out DateTime startTime, out string message)
        {
            sourceOpName = "";
            startTime = DateTime.MinValue;
            message = "";

            try
            {
                // 取 OP400/OP410/OP420 最后一条有效离开记录作为常温静置开始时间。
                string procedureName = "OP431手动静置出库_前序离开时间_查询";
                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 = "未查询到 OP400/OP410/OP420 前序出站记录";
                    return false;
                }

                sourceOpName = Convert.ToString(dt.Rows[0]["工位号"]);
                startTime = Convert.ToDateTime(dt.Rows[0]["开始静置时间"]);
                return true;
            }
            catch (Exception err)
            {
                message = "查询前序出站时间失败：" + err.Message;
                return false;
            }
        }

        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;
            }

            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;
        }

        private int NormalizeManualStaticQualityMask(int qualityMask)
        {
            return qualityMask == 1 ? 1 : 2;
        }

        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;
        }

        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;
            }

            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;
            }

            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;
        }

        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;
        }

        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;
        }

        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);

            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;
        }

        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);
            }
        }

        private void SendManualStaticPadMqttMessage(string command, string opName, string value)
        {
            SendMessage.AsyncSendNotOpMessage(ManualStaticPadTopic, "MES|" + command + "|" + opName + "|" + value);
        }

        private void RememberManualStaticMinutesForProduct(string engineID, decimal minStaticMinutes)
        {
            lock (ManualStaticMinutesLocker)
            {
                ManualStaticMinutesByEngine[engineID] = minStaticMinutes;
            }
        }

        private bool TryGetManualStaticMinutesForProduct(string engineID, out decimal minStaticMinutes)
        {
            lock (ManualStaticMinutesLocker)
            {
                if (ManualStaticMinutesByEngine.ContainsKey(engineID))
                {
                    minStaticMinutes = ManualStaticMinutesByEngine[engineID];
                    return true;
                }
            }

            minStaticMinutes = DefaultManualStaticMinutes;
            return false;
        }
    }
}
