using Serilog;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace Ayay.SerilogLogs
{
    /// <summary>
    /// 日志清理工具
    /// <para>弥补 Serilog 原生清理功能的不足，支持“按天数”和“按总大小”进行全局清理。</para>
    /// <para>✅ 已适配多级目录结构，会自动清理删除文件后留下的空文件夹。</para>
    /// </summary>
    public static class LogCleaner
    {
        /// <summary>
        /// 异步执行清理任务
        /// </summary>
        /// <param name="opts">配置选项</param>
        public static void RunAsync(LogOptions opts)
        {
            string rootPath = opts.LogRootPath;     // 日志根目录
            int maxDays = opts.MaxRetentionDays;    // 最大保留天数
            long maxBytes = opts.MaxTotalLogSize;   // 最大总字节数

            Task.Run(() =>
            {
                try
                {
                    CleanUp(rootPath, maxDays, maxBytes);
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "[LogCleaner] 日志自动清理任务执行失败");
                }
            });
        }

        private static void CleanUp(string rootPath, int maxDays, long maxBytes)
        {
            var dirInfo = new DirectoryInfo(rootPath);
            if (!dirInfo.Exists) return;

            // =========================================================
            // 第一步：清理文件 (递归查找所有子目录)
            // =========================================================

            // 获取所有 .txt 文件，按最后修改时间升序排列 (最旧的在前面)
            // SearchOption.AllDirectories 确保了能扫描到 2026-01-15 这种子文件夹里的内容
            var allFiles = dirInfo.GetFiles("*.txt", SearchOption.AllDirectories)
                                  .OrderBy(f => f.LastWriteTime)
                                  .ToList();

            if (allFiles.Count == 0) return;

            long currentTotalSize = 0;
            var cutOffDate = DateTime.Now.Date.AddDays(-maxDays); // 只保留到今天之前的 N 天
            int deletedCount = 0;

            // --- 策略 A: 按时间清理 ---
            for (int i = allFiles.Count - 1; i >= 0; i--)
            {
                var file = allFiles[i];

                if (file.LastWriteTime.Date < cutOffDate)
                {
                    try
                    {
                        file.Delete();
                        allFiles.RemoveAt(i);
                        deletedCount++;
                    }
                    catch { /* 忽略占用 */ }
                }
                else
                {
                    currentTotalSize += file.Length;
                }
            }

            if (deletedCount > 0)
            {
                Log.ForContext("SourceContext", LogModules.Core)
                   .Information("[LogCleaner] 时间策略: 已删除 {Count} 个过期文件", deletedCount);
            }

            // --- 策略 B: 按总大小清理 ---
            if (currentTotalSize > maxBytes)
            {
                int sizeDeletedCount = 0;
                long freedBytes = 0;

                foreach (var file in allFiles)
                {
                    if (currentTotalSize <= maxBytes) break;

                    try
                    {
                        long len = file.Length;
                        file.Delete();
                        currentTotalSize -= len;
                        freedBytes += len;
                        sizeDeletedCount++;
                    }
                    catch { /* 忽略占用 */ }
                }

                if (sizeDeletedCount > 0)
                {
                    Log.ForContext("SourceContext", LogModules.Core)
                       .Warning("[LogCleaner] 空间策略: 已删除 {Count} 个旧文件, 释放 {SizeMB:F2} MB",
                           sizeDeletedCount, freedBytes / 1024.0 / 1024.0);
                }
            }

            // =========================================================
            // 第二步：清理空文件夹 (新增逻辑)
            // =========================================================
            // 目的：当 2026-01-01 文件夹里的文件都被删光后，这个文件夹本身也应该被删掉
            DeleteEmptyDirectories(dirInfo);
        }

        /// <summary>
        /// 递归删除空文件夹
        /// </summary>
        private static void DeleteEmptyDirectories(DirectoryInfo dir)
        {
            // 1. 先递归处理子文件夹
            foreach (var subDir in dir.GetDirectories())
            {
                DeleteEmptyDirectories(subDir);
            }

            // 2. 检查当前文件夹是否为空 (且不是根目录自己)
            try
            {
                // 重新刷新状态
                dir.Refresh();

                // 如果没有文件 且 没有子文件夹
                if (dir.GetFiles().Length == 0 && dir.GetDirectories().Length == 0)
                {
                    // 注意：不要删除根目录 LogRootPath
                    // 这里虽然逻辑上递归会删到底，但通常外层调用时传入的是 Logs 根目录，
                    // 只要 LogBootstrapper 里保证 Logs 存在，这里删掉子目录没问题。
                    // 为了安全，可以判断一下是否是根目录的直接子级，或者简单地 try catch 忽略根目录无法删除的异常。

                    dir.Delete();
                }
            }
            catch
            {
                // 忽略异常 (比如文件夹正被打开，或者试图删除根目录但被占用)
            }
        }
    }
}