using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using MySqlConnector;
using Dapper;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using CncModels.Entity;
using CncModels.Dto.CollectLog;
using CncService.Interface;
using CncRepository.Interface;
using CncRepository.Impl.Log;
using CncRepository.Impl;
using CncRepository.Base;

namespace CncService.Impl
{
    /// <summary>
    /// 回放服务实现
    /// 通过日志库日活日志与业务库表实现回放能力
    /// 注意：此实现尽量复用现有 SQL，避免引入额外依赖
    /// </summary>
    public class ReplayService : IReplayService
    {
        private readonly string _businessConn;
        private readonly string _logConn;

        public ReplayService(string businessConn, string logConn)
        {
            _businessConn = businessConn ?? throw new ArgumentNullException(nameof(businessConn));
            _logConn = logConn ?? throw new ArgumentNullException(nameof(logConn));
        }

        /// <summary>预览回放影响范围（不做写操作）</summary>
        public ReplayPreview PreviewReplay(DateTime date)
        {
            using (var logConn = new MySqlConnection(_logConn))
            {
                logConn.Open();
                // 原始日志数量
                var rawCount = logConn.ExecuteScalar<int>(@"SELECT COUNT(1) FROM log_collect_raw WHERE DATE(request_time) = @Date AND is_success = 1", new { Date = date.Date });
                // 预计影响的记录/机器/段落数量
                var rebuiltCount = 0; // 预览阶段不写入，返回0
                using (var b = new MySqlConnection(_businessConn))
                {
                    b.Open();
                    var machineCount = b.ExecuteScalar<int>(@"SELECT COUNT(DISTINCT machine_id) FROM cnc_collect_record WHERE DATE(collect_time) = @Date", new { Date = date.Date });
                    var segmentCount = b.ExecuteScalar<int>(@"SELECT COUNT(1) FROM cnc_production_segment WHERE production_date = @Date", new { Date = date.Date });
                    var recCount = b.ExecuteScalar<int>(@"SELECT COUNT(1) FROM cnc_collect_record WHERE DATE(collect_time) = @Date", new { Date = date.Date });
                    return new ReplayPreview
                    {
                        Date = date.Date,
                        RawLogCount = rawCount,
                        AffectedMachineCount = machineCount,
                        AffectedRecordCount = recCount,
                        AffectedSegmentCount = segmentCount,
                    };
                }
            }
        }

        /// <summary>执行回放：清空当天数据并重新写入（简化实现）</summary>
        public ReplayResult ExecuteReplay(DateTime date)
        {
            int clearedRecordCount = 0;
            int clearedSegmentCount = 0;
            int rebuiltRecordCount = 0;
            try
            {
                // 1) 读取当天成功的原始日志
                List<CollectRaw> rawLogs;
                using (var logConn = new MySqlConnection(_logConn))
                {
                    logConn.Open();
                    string sql = @"SELECT * FROM log_collect_raw WHERE DATE(request_time) = @Date AND is_success = 1 ORDER BY request_time ASC";
                    rawLogs = logConn.Query<CollectRaw>(sql, new { Date = date.Date }).ToList();
                }

                // 2) 业务库清空（按外键依赖的顺序）
                using (var conn = new MySqlConnection(_businessConn))
                {
                    conn.Open();
                    using (var tran = conn.BeginTransaction())
                    {
                        // 2.1 清空依赖表
                        clearedRecordCount = conn.Execute("DELETE FROM cnc_production_adjustment WHERE DATE(created_at) = @Date", new { Date = date.Date }, tran);
                        // 逐表清空，确保单条语句执行兼容性
                        int c1 = conn.Execute("DELETE FROM cnc_worker_daily_summary WHERE production_date = @Date", new { Date = date.Date }, tran);
                        int c2 = conn.Execute("DELETE FROM cnc_daily_production WHERE production_date = @Date", new { Date = date.Date }, tran);
                        int c3 = conn.Execute("DELETE FROM cnc_machine_daily_status WHERE production_date = @Date", new { Date = date.Date }, tran);
                        int c4 = conn.Execute("DELETE FROM cnc_production_segment WHERE production_date = @Date", new { Date = date.Date }, tran);
                        int c5 = conn.Execute("DELETE FROM cnc_collect_record WHERE DATE(collect_time) = @Date", new { Date = date.Date }, tran);
                        clearedSegmentCount = c1;
                        tran.Commit();
                    }
                }

                // 3) 逐条 raw 日志解析并写入 cnc_collect_record（简化实现）
                using (var conn = new MySqlConnection(_businessConn))
                {
                    conn.Open();
                    foreach (var raw in rawLogs)
                    {
                        // 简单 JSON 解析，提取每个 device 的信息并写入 cnc_collect_record
                        try
                        {
                            var devices = JArray.Parse(raw.RawJson);
                            foreach (JObject d in devices)
                            {
                                string deviceCode = d?.Value<string>("device") ?? null;
                                if (string.IsNullOrWhiteSpace(deviceCode)) continue;
                                // 通过设备代码获取机器ID
                                var machineId = conn.QuerySingleOrDefault<int?>("SELECT id FROM cnc_machine WHERE device_code = @Code", new { Code = deviceCode });
                                if (machineId == null) continue;

                                // 收集 tag 值
                                var tags = d?.Value<JArray>("tags");
                                var programName = ExtractTagValue(tags, "Tag5");
                                var partCount = ParseDecimal(ExtractTagValue(tags, "Tag8"));
                                var runStatus = ExtractTagValue(tags, "Tag9");
                                var operateMode = ExtractTagValue(tags, "Tag11");
                                var spindleSet = ParseDecimal(ExtractTagValue(tags, "Tag17"));
                                var spindleActual = ParseDecimal(ExtractTagValue(tags, "Tag19"));
                                var machiningStatus = ExtractTagValue(tags, "Tag26");

                                var collectTime = raw.RequestTime;

                                var rec = new CollectRecord
                                {
                                    MachineId = machineId.Value,
                                    CollectTime = collectTime,
                                    ProgramName = programName,
                                    PartCount = partCount,
                                    RunStatus = runStatus,
                                    OperateMode = operateMode,
                                    SpindleSpeedSet = spindleSet,
                                    SpindleSpeedActual = spindleActual,
                                    MachiningStatus = machiningStatus,
                                    CreatedAt = DateTime.Now
                                };
                                string insertSql = @"INSERT INTO cnc_collect_record (machine_id, collect_time, program_name, part_count, run_status, operate_mode, spindle_speed_set, spindle_speed_actual, machining_status, created_at) VALUES (@MachineId, @CollectTime, @ProgramName, @PartCount, @RunStatus, @OperateMode, @SpindleSpeedSet, @SpindleSpeedActual, @MachiningStatus, @CreatedAt)";
                                conn.Execute(insertSql, new
                                {
                                    MachineId = rec.MachineId,
                                    CollectTime = rec.CollectTime,
                                    ProgramName = rec.ProgramName,
                                    PartCount = rec.PartCount,
                                    RunStatus = rec.RunStatus,
                                    OperateMode = rec.OperateMode,
                                    SpindleSpeedSet = rec.SpindleSpeedSet,
                                    SpindleSpeedActual = rec.SpindleSpeedActual,
                                    MachiningStatus = rec.MachiningStatus,
                                    CreatedAt = rec.CreatedAt
                                });
                                rebuiltRecordCount++;
                            }
                        }
                        catch
                        {
                            // 忽略单条日志的解析错误，继续处理下一条
                        }
                    }
                }

                // 4) 重新执行日终汇总（调用同样的 SQL 做聚合）
                using (var conn = new MySqlConnection(_businessConn))
                {
                    conn.Open();
                    // 以昨天日期执行日终汇总，使用 DailySummaryJob 风格的实现
                    // 结账活跃段
                    conn.Execute(@"UPDATE cnc_production_segment SET is_settled = 1, close_reason = 'replay' WHERE production_date = @Date AND is_settled = 0", new { Date = date.Date });
                    // 产量汇总（简化：重新计算所有段的 quantity）
                    conn.Execute(@"UPDATE cnc_production_segment SET quantity = GREATEST(0, COALESCE(end_part_count, 0) - start_part_count) WHERE production_date = @Date", new { Date = date.Date });
                    // 汇总日产量（简化版本）
                    conn.Execute(@"DELETE FROM cnc_daily_production WHERE production_date = @Date", new { Date = date.Date });
                    conn.Execute(@"INSERT INTO cnc_daily_production (machine_id, production_date, program_name, total_quantity, segment_count, created_at, updated_at) SELECT machine_id, production_date, program_name, SUM(quantity), COUNT(*), NOW(), NOW() FROM cnc_production_segment WHERE production_date = @Date GROUP BY machine_id, production_date, program_name", new { Date = date.Date });
                    // 更新机床日状态
                    conn.Execute(@"DELETE FROM cnc_machine_daily_status WHERE production_date = @Date", new { Date = date.Date });
                    conn.Execute(@"INSERT INTO cnc_machine_daily_status (machine_id, production_date, data_status, created_at, updated_at) SELECT machine_id, production_date, 'normal', NOW(), NOW() FROM cnc_daily_production WHERE production_date = @Date GROUP BY machine_id", new { Date = date.Date });
                    // 汇总员工日产量（简化）
                    conn.Execute(@"DELETE FROM cnc_worker_daily_summary WHERE production_date = @Date", new { Date = date.Date });
                    conn.Execute(@"INSERT INTO cnc_worker_daily_summary (worker_id, production_date, total_quantity, machine_count, program_count, created_at, updated_at) SELECT 0, production_date, SUM(total_quantity), COUNT(DISTINCT machine_id), COUNT(DISTINCT program_name), NOW(), NOW() FROM cnc_daily_production WHERE production_date = @Date", new { Date = date.Date });
                }

                return new ReplayResult
                {
                    Date = date.Date,
                    ClearedRecordCount = clearedRecordCount,
                    ClearedSegmentCount = clearedSegmentCount,
                    RebuiltRecordCount = rebuiltRecordCount,
                    Success = true
                };
            }
            catch (Exception ex)
            {
                return new ReplayResult
                {
                    Date = date.Date,
                    ClearedRecordCount = 0,
                    ClearedSegmentCount = 0,
                    RebuiltRecordCount = 0,
                    Success = false,
                    ErrorMessage = ex.Message
                };
            }
        }

        // helpers
        private static string ExtractTagValue(JArray tags, string id)
        {
            if (tags == null) return null;
            foreach (JObject t in tags)
            {
                if (t?.Value<string>("id") == id) return t?.Value<string>("value");
            }
            return null;
        }

        private static decimal? ParseDecimal(string s)
        {
            if (decimal.TryParse(s, out var d)) return d;
            return null;
        }
    }
}
