using System;
using System.Linq;
using System.Collections.Generic;
using System.Data;
using Dapper;
using CncModels.Dto.Dashboard;
using CncModels.Dto;
using CncRepository.Base;
using CncRepository.Interface;

namespace CncRepository.Impl.Dashboard
{
    /// <summary>
    /// 仪表盘跨表统计查询实现
    /// </summary>
    public class DashboardRepository : BusinessRepository, IDashboardRepository
    {
        public DashboardRepository(string connectionString) : base(connectionString) { }

        /// <summary>
        /// 通用产量计算SQL片段：已汇总日期用cnc_daily_production，未汇总日期用cnc_production_segment实时计算。
        /// 与machine表JOIN时，需在外层提供 machine_id 列。
        /// 参数：@StartDate, @EndDate
        /// 返回列：machine_id, production_date, day_quantity
        /// </summary>
        private const string DailyQuantityUnionSql = @"
            SELECT machine_id, production_date, day_quantity FROM (
                SELECT dp.machine_id, dp.production_date, SUM(dp.total_quantity) AS day_quantity
                FROM cnc_daily_production dp
                WHERE dp.production_date BETWEEN @StartDate AND @EndDate
                GROUP BY dp.machine_id, dp.production_date
            ) summarized
            UNION ALL
            SELECT seg.machine_id, seg.production_date,
                   SUM(CASE WHEN seg.is_settled=1 THEN seg.quantity
                        ELSE COALESCE(seg.end_part_count, seg.start_part_count) - seg.start_part_count END) AS day_quantity
            FROM cnc_production_segment seg
            WHERE seg.production_date BETWEEN @StartDate AND @EndDate
              AND NOT EXISTS (
                  SELECT 1 FROM cnc_daily_production dp
                  WHERE dp.machine_id = seg.machine_id AND dp.production_date = seg.production_date
              )
            GROUP BY seg.machine_id, seg.production_date
            ) all_days";

        /// <summary>汇总卡片数据</summary>
        public DashboardSummaryResponse GetSummary()
        {
            using (var conn = CreateConnection())
            {
                var onlineCount = conn.ExecuteScalar<int>(@"SELECT COUNT(1) FROM cnc_machine WHERE is_enabled = 1 AND last_ping_time IS NOT NULL AND last_ping_time >= NOW() - INTERVAL 20 SECOND");
                var totalMachines = conn.ExecuteScalar<int>(@"SELECT COUNT(1) FROM cnc_machine WHERE is_enabled = 1");
                // 今日总产量：直接从产量分段实时计算（今日一定没有日终汇总）
                var todayProduction = conn.ExecuteScalar<int>(@"
                    SELECT COALESCE(SUM(CASE WHEN is_settled=1 THEN quantity 
                        ELSE COALESCE(end_part_count,start_part_count) - start_part_count END), 0)
                    FROM cnc_production_segment WHERE production_date = CURDATE()");
                var activeAlerts = conn.ExecuteScalar<int>(@"SELECT COUNT(1) FROM cnc_alert WHERE is_resolved = 0");

                // 采集成功率：基于原始采集日志的成功/失败比率
                var successCount = conn.ExecuteScalar<int>(@"SELECT COUNT(1) FROM cnc_log.log_collect_raw WHERE is_success = 1 AND request_time >= CURDATE()");
                var failCount = conn.ExecuteScalar<int>(@"SELECT COUNT(1) FROM cnc_log.log_collect_raw WHERE is_success = 0 AND request_time >= CURDATE()");
                var totalCount = successCount + failCount;
                decimal collectSuccessRate = totalCount > 0 ? Math.Round((decimal)successCount / totalCount * 100, 2) : 0m;

                var runningMachines = conn.ExecuteScalar<int>(@"SELECT COUNT(1) FROM cnc_machine WHERE last_device_status = 'running'");
                var dataMissingMachines = conn.ExecuteScalar<int>(@"SELECT COUNT(1) FROM cnc_machine_daily_status WHERE production_date = CURDATE() AND data_status = 'data_missing'");

                return new DashboardSummaryResponse
                {
                    OnlineCount = onlineCount,
                    TotalMachines = totalMachines,
                    TodayProduction = todayProduction,
                    ActiveAlerts = activeAlerts,
                    CollectSuccessRate = collectSuccessRate,
                    RunningMachines = runningMachines,
                    DataMissingMachines = dataMissingMachines
                };
            }
        }

        /// <summary>按车间时间区间统计生产量（平均单机产量）</summary>
        public List<WorkshopProductionResponse> GetWorkshopProduction(DateTime startDate, DateTime endDate)
        {
            using (var conn = CreateConnection())
            {
                var sql = @"
                    SELECT w.Name AS WorkshopName,
                           COALESCE(SUM(ad.day_quantity), 0) AS Quantity,
                           COUNT(DISTINCT m.id) AS MachineCount,
                           CASE
                               WHEN DATEDIFF(@EndDate, @StartDate) = 0 
                                   THEN COALESCE(SUM(ad.day_quantity), 0) / NULLIF(COUNT(DISTINCT m.id), 0)
                               ELSE COALESCE(SUM(ad.day_quantity), 0) / (DATEDIFF(@EndDate, @StartDate) + 1) / NULLIF(COUNT(DISTINCT m.id), 0)
                           END AS AvgQuantity
                    FROM cnc_workshop w
                    LEFT JOIN cnc_machine m ON m.workshop_id = w.id AND m.is_enabled = 1
                    LEFT JOIN (
                        SELECT machine_id, production_date, day_quantity FROM (
                            SELECT dp.machine_id, dp.production_date, SUM(dp.total_quantity) AS day_quantity
                            FROM cnc_daily_production dp
                            WHERE dp.production_date BETWEEN @StartDate AND @EndDate
                            GROUP BY dp.machine_id, dp.production_date
                        ) summarized
                        UNION ALL
                        SELECT seg.machine_id, seg.production_date,
                               SUM(CASE WHEN seg.is_settled=1 THEN seg.quantity
                                    ELSE COALESCE(seg.end_part_count, seg.start_part_count) - seg.start_part_count END) AS day_quantity
                        FROM cnc_production_segment seg
                        WHERE seg.production_date BETWEEN @StartDate AND @EndDate
                          AND NOT EXISTS (
                              SELECT 1 FROM cnc_daily_production dp
                              WHERE dp.machine_id = seg.machine_id AND dp.production_date = seg.production_date
                          )
                        GROUP BY seg.machine_id, seg.production_date
                    ) ad ON ad.machine_id = m.id
                    GROUP BY w.id, w.name";
                return conn.Query<WorkshopProductionResponse>(sql, new { StartDate = startDate, EndDate = endDate }).ToList();
            }
        }

        /// <summary>机床排行</summary>
        public List<MachineRankResponse> GetMachineRank(DateTime startDate, DateTime endDate, int top, string sortOrder = "desc")
        {
            // 排序方向白名单校验，防止SQL注入
            var orderBy = string.Equals(sortOrder, "asc", StringComparison.OrdinalIgnoreCase) ? "ASC" : "DESC";
            using (var conn = CreateConnection())
            {
                var sql = $@"
                    SELECT m.id AS MachineId,
                           m.name AS MachineName,
                           COALESCE(SUM(ad.day_quantity), 0) AS Quantity,
                           (CASE WHEN m.last_ping_time IS NOT NULL AND m.last_ping_time >= NOW() - INTERVAL 20 SECOND THEN 1 ELSE 0 END) AS Status,
                           (SELECT seg.program_name FROM cnc_production_segment seg
                            WHERE seg.machine_id = m.id AND seg.production_date = CURDATE()
                            ORDER BY seg.id DESC LIMIT 1) AS Program
                    FROM cnc_machine m
                    LEFT JOIN (
                        SELECT machine_id, production_date, day_quantity FROM (
                            SELECT dp.machine_id, dp.production_date, SUM(dp.total_quantity) AS day_quantity
                            FROM cnc_daily_production dp
                            WHERE dp.production_date BETWEEN @StartDate AND @EndDate
                            GROUP BY dp.machine_id, dp.production_date
                        ) summarized
                        UNION ALL
                        SELECT seg.machine_id, seg.production_date,
                               SUM(CASE WHEN seg.is_settled=1 THEN seg.quantity
                                    ELSE COALESCE(seg.end_part_count, seg.start_part_count) - seg.start_part_count END) AS day_quantity
                        FROM cnc_production_segment seg
                        WHERE seg.production_date BETWEEN @StartDate AND @EndDate
                          AND NOT EXISTS (
                              SELECT 1 FROM cnc_daily_production dp
                              WHERE dp.machine_id = seg.machine_id AND dp.production_date = seg.production_date
                          )
                        GROUP BY seg.machine_id, seg.production_date
                    ) ad ON ad.machine_id = m.id
                    GROUP BY m.id, m.name, m.is_enabled, m.last_ping_time
                    ORDER BY Quantity {orderBy}
                    LIMIT @Top";
                var rows = conn.Query<MachineRankResponse>(sql, new { StartDate = startDate, EndDate = endDate, Top = top }).ToList();
                // 填充排名
                for (int i = 0; i < rows.Count; i++) rows[i].Rank = i + 1;
                return rows;
            }
        }

        /// <summary>工人排行</summary>
        public List<WorkerRankResponse> GetWorkerRank(DateTime startDate, DateTime endDate, int top, string sortOrder = "desc")
        {
            // 排序方向白名单校验，防止SQL注入
            var orderBy = string.Equals(sortOrder, "asc", StringComparison.OrdinalIgnoreCase) ? "ASC" : "DESC";
            using (var conn = CreateConnection())
            {
                var sql = $@"
                    SELECT w.name AS WorkerName,
                           COUNT(DISTINCT wm.machine_id) AS MachineCount,
                           COALESCE(SUM(ad.day_quantity), 0) AS TotalQuantity
                    FROM cnc_worker w
                    LEFT JOIN cnc_worker_machine wm ON wm.worker_id = w.id
                    LEFT JOIN (
                        SELECT machine_id, production_date, day_quantity FROM (
                            SELECT dp.machine_id, dp.production_date, SUM(dp.total_quantity) AS day_quantity
                            FROM cnc_daily_production dp
                            WHERE dp.production_date BETWEEN @StartDate AND @EndDate
                            GROUP BY dp.machine_id, dp.production_date
                        ) summarized
                        UNION ALL
                        SELECT seg.machine_id, seg.production_date,
                               SUM(CASE WHEN seg.is_settled=1 THEN seg.quantity
                                    ELSE COALESCE(seg.end_part_count, seg.start_part_count) - seg.start_part_count END) AS day_quantity
                        FROM cnc_production_segment seg
                        WHERE seg.production_date BETWEEN @StartDate AND @EndDate
                          AND NOT EXISTS (
                              SELECT 1 FROM cnc_daily_production dp
                              WHERE dp.machine_id = seg.machine_id AND dp.production_date = seg.production_date
                          )
                        GROUP BY seg.machine_id, seg.production_date
                    ) ad ON ad.machine_id = wm.machine_id
                    GROUP BY w.id, w.name
                    ORDER BY TotalQuantity {orderBy}
                    LIMIT @Top";
                var rows = conn.Query<WorkerRankResponse>(sql, new { StartDate = startDate, EndDate = endDate, Top = top }).ToList();
                // 填充排名
                for (int i = 0; i < rows.Count; i++) rows[i].Rank = i + 1;
                return rows;
            }
        }

        /// <summary>产量趋势（最近 days 天）</summary>
        public List<dynamic> GetProductionTrend(int days)
        {
            using (var conn = CreateConnection())
            {
                // 已汇总日期用daily_production，未汇总日期用segment实时计算
                var sql = @"
                    SELECT production_date AS Date, COALESCE(SUM(day_quantity), 0) AS Quantity
                    FROM (
                        SELECT dp.production_date, SUM(dp.total_quantity) AS day_quantity
                        FROM cnc_daily_production dp
                        WHERE dp.production_date >= DATE_SUB(CURDATE(), INTERVAL @Days DAY)
                        GROUP BY dp.production_date
                        UNION ALL
                        SELECT seg.production_date,
                               SUM(CASE WHEN seg.is_settled=1 THEN seg.quantity
                                    ELSE COALESCE(seg.end_part_count, seg.start_part_count) - seg.start_part_count END) AS day_quantity
                        FROM cnc_production_segment seg
                        WHERE seg.production_date >= DATE_SUB(CURDATE(), INTERVAL @Days DAY)
                          AND NOT EXISTS (
                              SELECT 1 FROM cnc_daily_production dp
                              WHERE dp.production_date = seg.production_date
                          )
                        GROUP BY seg.production_date
                    ) all_days
                    GROUP BY production_date
                    ORDER BY production_date ASC";
                return conn.Query(sql, new { Days = days }).ToList();
            }
        }

        /// <summary>机床状态分布</summary>
        public object GetMachineStatusDistribution()
        {
            using (var conn = CreateConnection())
            {
                var online = conn.ExecuteScalar<int>("SELECT COUNT(1) FROM cnc_machine WHERE is_enabled = 1 AND last_ping_time IS NOT NULL AND last_ping_time >= NOW() - INTERVAL 20 SECOND");
                var offline = conn.ExecuteScalar<int>("SELECT COUNT(1) FROM cnc_machine WHERE is_enabled = 1 AND (last_ping_time IS NULL OR last_ping_time < NOW() - INTERVAL 20 SECOND)");
                var disabled = conn.ExecuteScalar<int>("SELECT COUNT(1) FROM cnc_machine WHERE is_enabled = 0");
                return new { online, offline, disabled };
            }
        }

        /// <summary>最近告警</summary>
        public List<AlertListItem> GetRecentAlerts(int count)
        {
            using (var conn = CreateConnection())
            {
                var sql = @"SELECT a.id AS Id, a.created_at AS CreatedAt, a.alert_type AS AlertType,
                            COALESCE(m.name, '') AS MachineName, a.title AS Title,
                            CAST(a.is_resolved AS SIGNED) AS IsResolved
                            FROM cnc_alert a
                            LEFT JOIN cnc_machine m ON m.id = a.machine_id
                            ORDER BY a.created_at DESC LIMIT @Count";
                return conn.Query<AlertListItem>(sql, new { Count = count }).ToList();
            }
        }
    }
}
