using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Dapper;
using MySqlConnector;
using CncModels.Entity;
using CncModels.Enum;
using log4net;

namespace CncCollector.Core
{
    /// <summary>
    /// 零件产量分段跟踪引擎。
    /// 为每台机床维护内存中的当前活跃段状态，检测程序切换和手动清零。
    /// </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>
        /// 内存缓存：machineId → 当前活跃段ID（减少DB查询）
        /// </summary>
        private readonly ConcurrentDictionary<int, long> _activeSegmentIds = new ConcurrentDictionary<int, long>();

        /// <summary>
        /// 内存缓存：machineId → 上一次采集的 (programName, partCount)
        /// </summary>
        private readonly ConcurrentDictionary<int, Tuple<string, decimal?>> _lastCollectState = new ConcurrentDictionary<int, Tuple<string, decimal?>>();

        /// <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="partCount">当前零件数</param>
        /// <param name="collectTime">采集时间</param>
        public void Track(int machineId, string programName, decimal? partCount, DateTime collectTime)
        {
            if (string.IsNullOrEmpty(programName)) return;

            lock (_lock)
            {
                try
                {
                    // 获取上次采集状态
                    Tuple<string, decimal?> lastState;
                    bool hasLast = _lastCollectState.TryGetValue(machineId, out lastState);

                    // 检测是否需要结账
                    bool needClose = false;
                    string closeReason = "";

                    if (hasLast)
                    {
                        string lastProgram = lastState.Item1;
                        decimal? lastPartCount = lastState.Item2;

                        // 情况1：NC程序名变化
                        if (!string.Equals(lastProgram, programName, StringComparison.OrdinalIgnoreCase))
                        {
                            needClose = true;
                            closeReason = SegmentCloseReason.ProgramChange;
                            _log.Info($"机床{machineId}: 程序切换 {lastProgram} → {programName}（{closeReason}）");
                        }
                        // 情况2：同程序下 part_count 下降（手动清零）
                        else if (partCount.HasValue && lastPartCount.HasValue && partCount.Value < lastPartCount.Value)
                        {
                            needClose = true;
                            closeReason = SegmentCloseReason.ManualReset;
                            _log.Info($"机床{machineId}: 零件数下降 {lastPartCount} → {partCount}（{closeReason}）");
                        }
                    }

                    // 结账当前活跃段
                    // 修复：使用结账前最后已知的零件数（lastPartCount），而非当前观测值（partCount）
                    // 当 manual_reset 时 partCount 是复位后的值（如0），当 program_change 时 partCount 是新程序的值
                    // 正确的 endPartCount 应该是结账段最后观测到的最大计数值
                    if (needClose)
                    {
                        var closingEndCount = hasLast ? lastState.Item2 : partCount;
                        CloseActiveSegment(machineId, closingEndCount, closeReason, collectTime);
                    }

                    // 确保有活跃段
                    long activeSegmentId = EnsureActiveSegment(machineId, programName, partCount, collectTime);

                    // 更新活跃段的实时 end_part_count
                    if (activeSegmentId > 0 && partCount.HasValue)
                    {
                        UpdateSegmentEndCount(activeSegmentId, partCount.Value);
                    }

                    // 更新内存缓存
                    _lastCollectState[machineId] = Tuple.Create(programName, partCount);
                }
                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 { /* 告警写入失败不影响主流程 */ }
                }
            }
        }

        /// <summary>
        /// 服务停止时结账所有活跃段
        /// </summary>
        public void CloseAllActiveSegments()
        {
            _log.Info("正在结账所有活跃段（服务停止）...");

            try
            {
                using (var conn = new MySqlConnection(_connectionString))
                {
                    // 结账所有活跃段：is_settled=0 且 end_time IS NULL
                    // 正确计算quantity：保留当前end_part_count，用end-start计算产量
                    conn.Execute(@"UPDATE cnc_production_segment 
                        SET end_time = NOW(), 
                            end_part_count = COALESCE(end_part_count, start_part_count),
                            quantity = GREATEST(0, COALESCE(end_part_count, start_part_count) - start_part_count), 
                            close_reason = @Reason, is_settled = 1, updated_at = NOW()
                        WHERE is_settled = 0 AND end_time IS NULL",
                        new { Reason = SegmentCloseReason.ServiceStop });
                }

                _activeSegmentIds.Clear();
                _lastCollectState.Clear();
                _log.Info("所有活跃段已结账");
            }
            catch (Exception ex)
            {
                _log.Error("结账所有活跃段失败", ex);
            }
        }

        /// <summary>
        /// 结账指定机床的活跃段
        /// </summary>
        /// <param name="machineId">机床ID</param>
        /// <param name="endPartCount">结束时的零件数</param>
        /// <param name="reason">关闭原因</param>
        /// <param name="endTime">结束时间</param>
        public void CloseActiveSegment(int machineId, decimal? endPartCount, string reason, DateTime endTime)
        {
            try
            {
                long segmentId;
                if (!_activeSegmentIds.TryGetValue(machineId, out segmentId))
                {
                    // 从DB查找
                    using (var conn = new MySqlConnection(_connectionString))
                    {
                        segmentId = conn.ExecuteScalar<long>(
                            "SELECT id FROM cnc_production_segment WHERE machine_id=@MId AND is_settled=0 AND end_time IS NULL ORDER BY start_time DESC LIMIT 1",
                            new { MId = machineId });
                    }
                }

                if (segmentId <= 0) return;

                using (var conn = new MySqlConnection(_connectionString))
                {
                    // 获取 start_part_count 计算 quantity
                    var startCount = conn.ExecuteScalar<decimal?>(
                        "SELECT start_part_count FROM cnc_production_segment WHERE id=@Id", new { Id = segmentId });

                    decimal? quantity = null;
                    if (startCount.HasValue && endPartCount.HasValue)
                    {
                        quantity = Math.Max(0, endPartCount.Value - startCount.Value);
                    }

                    conn.Execute(@"UPDATE cnc_production_segment 
                        SET end_time = @EndTime, end_part_count = @EndPartCount, quantity = @Quantity,
                            close_reason = @Reason, is_settled = 1, updated_at = NOW()
                        WHERE id = @Id",
                        new { Id = segmentId, EndTime = endTime, EndPartCount = endPartCount, Quantity = quantity, Reason = reason });
                }

                _activeSegmentIds.TryRemove(machineId, out _);
                _log.Debug($"机床{machineId}: 结账段{segmentId}（{reason}，quantity={endPartCount}）");
            }
            catch (Exception ex)
            {
                _log.Error($"结账活跃段失败（machine_id={machineId}）", ex);
            }
        }

        /// <summary>
        /// 确保指定机床有一个活跃段，没有则创建
        /// </summary>
        private long EnsureActiveSegment(int machineId, string programName, decimal? partCount, DateTime collectTime)
        {
            long segmentId;

            if (_activeSegmentIds.TryGetValue(machineId, out segmentId) && segmentId > 0)
                return segmentId;

            // 从DB查找
            using (var conn = new MySqlConnection(_connectionString))
            {
                segmentId = conn.ExecuteScalar<long>(
                    "SELECT id FROM cnc_production_segment WHERE machine_id=@MId AND is_settled=0 AND end_time IS NULL ORDER BY start_time DESC LIMIT 1",
                    new { MId = machineId });

                if (segmentId <= 0)
                {
                    // 创建新段
                    var now = DateTime.Now;
                    segmentId = conn.ExecuteScalar<long>(@"INSERT INTO cnc_production_segment 
                        (machine_id, program_name, production_date, start_time, start_part_count, is_settled, created_at, updated_at)
                        VALUES (@MachineId, @ProgramName, @ProductionDate, @StartTime, @StartPartCount, 0, @CreatedAt, @UpdatedAt);
                        SELECT LAST_INSERT_ID();",
                        new
                        {
                            MachineId = machineId,
                            ProgramName = programName,
                            ProductionDate = collectTime.Date,
                            StartTime = collectTime,
                            StartPartCount = partCount ?? 0,
                            CreatedAt = now,
                            UpdatedAt = now
                        });

                    _log.Debug($"机床{machineId}: 创建新段{segmentId}（程序={programName}, startCount={partCount}）");
                }
            }

            _activeSegmentIds[machineId] = segmentId;
            return segmentId;
        }

        /// <summary>
        /// 更新活跃段的实时 end_part_count
        /// </summary>
        private void UpdateSegmentEndCount(long segmentId, decimal partCount)
        {
            try
            {
                using (var conn = new MySqlConnection(_connectionString))
                {
                    conn.Execute("UPDATE cnc_production_segment SET end_part_count = @Count, updated_at = NOW() WHERE id = @Id",
                        new { Id = segmentId, Count = partCount });
                }
            }
            catch (Exception ex)
            {
                _log.Error($"更新活跃段end_part_count失败（segment_id={segmentId}）", ex);
            }
        }

        public void Dispose()
        {
            CloseAllActiveSegments();
        }
    }
}
