using System;
using Dapper;
using MySqlConnector;
using log4net;

namespace CncCollector.Core
{
    /// <summary>
    /// 零件产量跟踪引擎。
    /// 基于数据库 cnc_daily_production 表对比加工零件总数(Tag1)计算当日产量。
    /// 记录不可变：每条记录创建后仅 end_total_count 可更新。
    /// </summary>
    public class ProductionTracker : IDisposable
    {
        private static readonly ILog _log = LogManager.GetLogger(typeof(ProductionTracker));
        private readonly string _connectionString;
        private readonly object _lock = new object();

        /// <summary>
        /// 初始化产量跟踪器
        /// </summary>
        /// <param name="connectionString">业务库连接字符串</param>
        public ProductionTracker(string connectionString)
        {
            _connectionString = connectionString;
        }

        /// <summary>
        /// 处理一次采集结果：基于数据库最新记录对比加工零件总数，计算当日产量。
        /// </summary>
        /// <param name="machineId">机床ID</param>
        /// <param name="programName">当前NC程序名</param>
        /// <param name="totalPartCount">加工零件总数(Tag1)</param>
        /// <param name="collectTime">采集时间</param>
        /// <returns>
        /// logText：描述本次处理结果的文本
        /// changed：数据库是否发生了变更
        /// todayTotal：当前机床当日总产量 = SUM(end_total_count - base_total_count)
        /// </returns>
        public (string logText, bool changed, decimal todayTotal) Track(
            int machineId, string programName, decimal? totalPartCount, DateTime collectTime)
        {
            // 前置校验：程序名为空 或 零件总数为空 → 跳过
            if (string.IsNullOrEmpty(programName) || !totalPartCount.HasValue)
            {
                var skipReason = string.IsNullOrEmpty(programName)
                    ? "程序名为空"
                    : "加工零件总数为空";
                _log.Debug($"机床{machineId}: 跳过产量跟踪（{skipReason}）");
                return ($"跳过：{skipReason}", false, 0);
            }

            lock (_lock)
            {
                try
                {
                    string logText;
                    bool changed;

                    using (var conn = new MySqlConnection(_connectionString))
                    {
                        conn.Open();

                        // 查询当天最新一条记录
                        var record = GetCurrentRecord(conn, machineId);

                        if (record == null)
                        {
                            // 当天首次采集 → 创建记录
                            InsertRecord(conn, machineId, programName,
                                totalPartCount.Value, totalPartCount.Value);
                            logText = "当天首次采集，创建记录";
                            changed = true;
                        }
                        else if (record.ProductionDate != DateTime.Today)
                        {
                            // 防御性检查：跨天（理论上不会发生，因为查询条件限定了 CURDATE()）
                            InsertRecord(conn, machineId, programName,
                                totalPartCount.Value, totalPartCount.Value);
                            logText = "跨天首次采集，创建记录";
                            changed = true;
                        }
                        else if (string.Equals(record.ProgramName, programName, StringComparison.OrdinalIgnoreCase))
                        {
                            (logText, changed) = HandleSameProgram(conn, record, totalPartCount.Value);
                        }
                        else
                        {
                            (logText, changed) = HandleProgramChange(conn, record, programName, totalPartCount.Value);
                        }

                        // 计算当日总产量 = 当天所有记录的 (end - base) 之和
                        var todayTotal = conn.ExecuteScalar<decimal>(
                            @"SELECT COALESCE(SUM(end_total_count - base_total_count), 0)
                              FROM cnc_daily_production
                              WHERE machine_id = @Mid AND production_date = CURDATE()",
                            new { Mid = machineId });

                        _log.Info($"机床{machineId}: {logText}（当日累计产量={todayTotal:F5}）");
                        return (logText, changed, todayTotal);
                    }
                }
                catch (Exception ex)
                {
                    _log.Error($"产量跟踪处理失败（machine_id={machineId}）", ex);

                    // 写入告警
                    try
                    {
                        using (var conn2 = new MySqlConnection(_connectionString))
                        {
                            conn2.Execute(
                                @"INSERT INTO cnc_alert (alert_type, machine_id, title, detail, is_resolved, created_at)
                                  VALUES (@Type, @Mid, @Title, @Detail, 0, NOW())",
                                new
                                {
                                    Type = "production_error",
                                    Mid = machineId,
                                    Title = "产量跟踪处理异常",
                                    Detail = $"机床{machineId}产量跟踪失败: {ex.Message}"
                                });
                        }
                    }
                    catch { /* 告警写入失败不影响主流程 */ }

                    return ($"异常：{ex.Message}", false, 0);
                }
            }
        }

        /// <summary>
        /// 查询当天最新一条日产量记录
        /// </summary>
        private DailyProductionRecord GetCurrentRecord(MySqlConnection conn, int machineId)
        {
            return conn.QueryFirstOrDefault<DailyProductionRecord>(
                @"SELECT id AS Id, machine_id AS MachineId, production_date AS ProductionDate,
                         program_name AS ProgramName, base_total_count AS BaseTotalCount,
                         end_total_count AS EndTotalCount
                  FROM cnc_daily_production
                  WHERE machine_id = @Mid AND production_date = CURDATE()
                  ORDER BY id DESC
                  LIMIT 1",
                new { Mid = machineId });
        }

        /// <summary>
        /// 处理同程序情况：对比 end_total_count 与当前的 totalPartCount
        /// </summary>
        private (string logText, bool changed) HandleSameProgram(
            MySqlConnection conn, DailyProductionRecord record, decimal totalPartCount)
        {
            if (totalPartCount > record.EndTotalCount)
            {
                // 正常加工增长 → 更新结束计数
                conn.Execute(
                    "UPDATE cnc_daily_production SET end_total_count = @Count WHERE id = @Id",
                    new { Id = record.Id, Count = totalPartCount });
                return ("正常加工，更新结束总零件数", true);
            }

            if (totalPartCount == record.EndTotalCount)
            {
                // 无变化 → 跳过
                return ("无变化", false);
            }

            // totalPartCount < record.EndTotalCount → 数据异常，不写库
            _log.Warn($"机床{record.MachineId}: 数据异常，加工零件总数从{record.EndTotalCount}变为{totalPartCount}，不更新数据库");
            return ("数据异常：加工零件总数变小", false);
        }

        /// <summary>
        /// 处理程序切换：旧程序→新程序，创建新记录
        /// </summary>
        private (string logText, bool changed) HandleProgramChange(
            MySqlConnection conn, DailyProductionRecord oldRecord,
            string newProgramName, decimal totalPartCount)
        {
            if (totalPartCount >= oldRecord.EndTotalCount)
            {
                // 合法程序切换 → 创建新记录
                // 关键：新记录的 base 使用旧记录的 end，保证产量连续
                InsertRecord(conn, oldRecord.MachineId, newProgramName,
                    oldRecord.EndTotalCount, totalPartCount);
                _log.Info($"机床{oldRecord.MachineId}: 程序切换 {oldRecord.ProgramName} → {newProgramName}，" +
                          $"新记录 base={oldRecord.EndTotalCount} end={totalPartCount}");
                return ("程序切换，创建新记录", true);
            }

            // totalPartCount < oldRecord.EndTotalCount → 数据异常，不写库
            _log.Warn($"机床{oldRecord.MachineId}: 程序切换时数据异常，" +
                      $"旧程序{oldRecord.ProgramName} end={oldRecord.EndTotalCount}，" +
                      $"新程序{newProgramName} total={totalPartCount}");
            return ("数据异常", false);
        }

        /// <summary>
        /// 插入一条新的日产量记录
        /// </summary>
        private void InsertRecord(MySqlConnection conn, int machineId,
            string programName, decimal baseTotalCount, decimal endTotalCount)
        {
            conn.Execute(
                @"INSERT INTO cnc_daily_production
                  (machine_id, production_date, program_name, base_total_count, end_total_count, created_at, updated_at)
                  VALUES (@MachineId, CURDATE(), @ProgramName, @BaseCount, @EndCount, NOW(), NOW())",
                new
                {
                    MachineId = machineId,
                    ProgramName = programName,
                    BaseCount = baseTotalCount,
                    EndCount = endTotalCount
                });
        }

        /// <summary>
        /// 日产量记录的数据库行映射
        /// </summary>
        private class DailyProductionRecord
        {
            public int Id { get; set; }
            public int MachineId { get; set; }
            public DateTime ProductionDate { get; set; }
            public string ProgramName { get; set; }
            public decimal BaseTotalCount { get; set; }
            public decimal EndTotalCount { get; set; }
        }

        /// <summary>
        /// 释放资源（当前无需要清理的资源）
        /// </summary>
        public void Dispose()
        {
            // 无内存缓存需清理，连接由 using 管理
        }
    }
}
