using System;
using System.Collections.Generic;
using Dapper;
using MySqlConnector;
using CncCollector.Config;
using log4net;

namespace CncCollector.Jobs
{
    /// <summary>
    /// 日志清理定时任务。
    /// 对按月分区表使用 DROP PARTITION 清理（瞬间完成），
    /// 对非分区表回退到 DELETE。保留天数=0表示不删除。
    /// </summary>
    public class LogCleanupJob
    {
        private static readonly ILog _log = LogManager.GetLogger(typeof(LogCleanupJob));

        private readonly string _logConnection;
        private readonly CollectorConfig _config;

        public LogCleanupJob(string logConnection, CollectorConfig config)
        {
            _logConnection = logConnection;
            _config = config;
        }

        /// <summary>
        /// 执行日志清理
        /// </summary>
        public void Execute()
        {
            try
            {
                int totalPartitions = 0;
                int totalRows = 0;
                using (var conn = new MySqlConnection(_logConnection))
                {
                    // 1) 采集分析日志（分区表：DROP PARTITION）
                    int daysA = Math.Max(_config.AnalysisLogRetentionDays, 0);
                    if (daysA > 0)
                    {
                        int dropped = DropOldPartitions(conn, "log_collect_analysis", "analysis_time", daysA);
                        if (dropped > 0)
                        {
                            totalPartitions += dropped;
                            _log.Info($"日志清理: log_collect_analysis DROP {dropped} 个分区，保留 {daysA} 天");
                        }
                    }

                    // 2) 采集周期日志（分区表：DROP PARTITION）
                    int daysC = Math.Max(_config.CycleLogRetentionDays, 0);
                    if (daysC > 0)
                    {
                        int dropped = DropOldPartitions(conn, "log_collect_cycle", "cycle_time", daysC);
                        if (dropped > 0)
                        {
                            totalPartitions += dropped;
                            _log.Info($"日志清理: log_collect_cycle DROP {dropped} 个分区，保留 {daysC} 天");
                        }
                    }

                    // 3) 原始日志（分区表：DROP PARTITION）
                    int daysR = Math.Max(_config.RawLogRetentionDays, 0);
                    if (daysR > 0)
                    {
                        int dropped = DropOldPartitions(conn, "log_collect_raw", "request_time", daysR);
                        if (dropped > 0)
                        {
                            totalPartitions += dropped;
                            _log.Info($"日志清理: log_collect_raw DROP {dropped} 个分区，保留 {daysR} 天");
                        }
                    }
                }
                _log.Info($"日志清理完成，DROP {totalPartitions} 个分区");
            }
            catch (Exception ex)
            {
                _log.Error("执行日志清理任务失败", ex);
            }
        }

        /// <summary>
        /// 清理过期的月分区。计算保留天数对应的截止月份，
        /// 找出所有分区边界早于截止月份的分区（排除p_future），执行 DROP PARTITION。
        /// </summary>
        private int DropOldPartitions(MySqlConnection conn, string tableName, string partitionColumn, int retentionDays)
        {
            int dropped = 0;
            try
            {
                // 截止日期：保留天数之前的日期
                var cutoffDate = DateTime.Now.AddDays(-retentionDays);
                // 截止月份的第一天（整个月都要删除）
                var cutoffMonth = new DateTime(cutoffDate.Year, cutoffDate.Month, 1);

                // 查询所有非 p_future 分区及其边界值
                var partitions = conn.Query<(string PARTITION_NAME, string PARTITION_DESCRIPTION)>(
                    @"SELECT PARTITION_NAME, PARTITION_DESCRIPTION 
                      FROM information_schema.PARTITIONS 
                      WHERE TABLE_SCHEMA = 'cnc_log' 
                        AND TABLE_NAME = @TableName 
                        AND PARTITION_NAME IS NOT NULL 
                        AND PARTITION_NAME <> 'p_future'
                      ORDER BY PARTITION_ORDINAL_POSITION",
                    new { TableName = tableName });

                foreach (var part in partitions)
                {
                    // PARTITION_DESCRIPTION 是 TO_DAYS('YYYY-MM-DD') 的整数值
                    if (!long.TryParse(part.PARTITION_DESCRIPTION, out long toDaysValue))
                        continue;

                    // 将 TO_DAYS 值反算为日期（近似：用 MySQL 的 FROM_DAYS）
                    DateTime partitionBoundary;
                    try
                    {
                        partitionBoundary = conn.ExecuteScalar<DateTime>(
                            "SELECT FROM_DAYS(@ToDays)", new { ToDays = toDaysValue });
                    }
                    catch
                    {
                        // 无法解析边界值，跳过此分区
                        _log.Warn($"无法解析分区边界值: {tableName}.{part.PARTITION_NAME} = {part.PARTITION_DESCRIPTION}");
                        continue;
                    }

                    // 如果分区边界 <= 截止月份，说明这个分区整月都在保留期外，可以 DROP
                    if (partitionBoundary <= cutoffMonth)
                    {
                        try
                        {
                            conn.Execute($"ALTER TABLE cnc_log.{tableName} DROP PARTITION {part.PARTITION_NAME}");
                            dropped++;
                            _log.Info($"DROP PARTITION: {tableName}.{part.PARTITION_NAME} (边界={partitionBoundary:yyyy-MM-dd})");
                        }
                        catch (Exception ex)
                        {
                            _log.Error($"DROP PARTITION 失败: {tableName}.{part.PARTITION_NAME}", ex);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                _log.Error($"清理分区表 {tableName} 时出错", ex);
            }
            return dropped;
        }
    }
}
