package vqdtask

import (
	"easyvqd/pkg/vqdcms"
	"fmt"
	"io/fs"
	"log/slog"
	"os"
	"path/filepath"
	"time"
)

// 配置参数
const (
	// 要清理的目标目录（请替换为你实际的目录路径）
	// 定时任务执行间隔（每天执行一次）
	interval = 24 * time.Hour
	// 批量删除大小（避免单次删除过多锁表）
	batchSize = 1000
	// 日期目录的格式（如 20260117）
	dateDirLayout = "20060102"
	// 定时任务首次执行时间（每天凌晨1点）
	executeHour = 1
)

// scheduleCleanFile 定时执行清理任务
func (c Core) scheduleCleanTask() {
	// 计算首次执行时间（今天/明天的凌晨1点）
	now := time.Now()
	nextExec := time.Date(now.Year(), now.Month(), now.Day(), executeHour, 0, 0, 0, now.Location())
	if nextExec.Before(now) {
		nextExec = nextExec.Add(24 * time.Hour)
	}

	// 计算首次执行的等待时间
	initialDelay := nextExec.Sub(now)
	slog.Info(fmt.Sprintf("定时任务已启动，首次执行时间：%s（等待 %v）", nextExec.Format(time.RFC3339), initialDelay))

	// 首次执行等待
	time.Sleep(initialDelay)

	// 执行首次清理
	if err := c.cleanExpiredFiles(); err != nil {
		slog.Error("首次清理任务执行失败 Files", "err", err)
	}
	time.Sleep(20 * time.Minute)
	if err := c.cleanExpiredDbs(); err != nil {
		slog.Error("首次清理任务执行失败 Dbs", "err", err)
	}

	// 循环执行定时任务
	ticker := time.NewTicker(interval)
	defer ticker.Stop()

	for range ticker.C {
		if err := c.cleanExpiredFiles(); err != nil {
			slog.Error("定时清理任务执行失败 Files", "err", err)
		}
		time.Sleep(20 * time.Minute)
		if err := c.cleanExpiredDbs(); err != nil {
			slog.Error("定时清理任务执行失败 Dbs", "err", err)
		}
	}
}

// cleanExpiredDbs 清理超过expireDays天的数据
func (c Core) cleanExpiredDbs() error {
	// 计算过期时间阈值
	expireDays := time.Duration(c.Cfg.VqdConfig.SaveDay)
	if expireDays < 1 {
		expireDays = 1
	}
	expireTime := time.Now().Add(-expireDays * 24 * time.Hour)
	slog.Info(fmt.Sprintf("开始清理中超过 %d 天的数据，过期时间阈值：%s", expireDays, expireTime.Format(time.RFC3339)))

	totalDeleted := 0

	for {
		deletedCount, err := c.VqdTaskCore.DelVqdTaskAlarmAll(expireTime, batchSize)
		if err != nil {
			slog.Error("数据清理失败", "err", err)
			return err
		}
		// 获取本次删除的行数
		totalDeleted += deletedCount

		// 无更多数据则退出循环
		if deletedCount < batchSize {
			break
		}

		// 批量删除间隔，降低数据库压力
		time.Sleep(100 * time.Millisecond)
	}
	slog.Info("本次数据清理任务执行完成")
	return nil
}

// deleteDirContents 删除目录下的所有文件（保留目录结构，仅删文件）
func deleteTaskDirContents(dir string, expireTime time.Time) error {
	// 遍历目录
	err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
		// 处理遍历过程中的错误（如权限问题）
		if err != nil {
			slog.Error("访问路径失败", "path", path, "err", err)
			return nil
		}
		// 只处理目录（跳过文件）
		if !d.IsDir() {
			return nil
		}
		// 获取当前目录的名称（如 20260117）
		dirName := filepath.Base(path)
		dirDate, err := time.Parse(dateDirLayout, dirName)
		if err != nil {
			slog.Error("解析目录日期失败", "path", path, "err", err)
			return nil
		}
		// 判断日期目录是否早于阈值（即过期）
		isExpired := dirDate.Before(expireTime)

		// 非日期目录，继续遍历子目录
		if dirDate.IsZero() {
			return nil
		}

		// 日期目录未过期，跳过
		if !isExpired {
			slog.Error("目录日期未过期跳过", "path", path, "dirDate", dirDate.Format(dateDirLayout))
			return nil
		}

		// 过期日期目录：先删除目录内所有文件
		slog.Error("目录已过期开始清理其中文件", "path", path, "dirDate", dirDate.Format(dateDirLayout))
		if err := deleteDirContents(path, expireTime); err != nil {
			slog.Error("清理目录内容失败", "path", path, "err", err)
			return nil
		}

		// 删除空的日期目录
		if err := os.Remove(path); err != nil {
			slog.Error("删除空目录失败（可能非空）", "path", path, "err", err)
		} else {
			slog.Info("成功删除过期目录", "path", path)
		}

		// 跳过已删除目录的子目录遍历（避免无效操作）
		return fs.SkipDir
	})
	if err != nil {
		slog.Error("遍历目录失败", "dir", dir, "err", err)
		return fmt.Errorf("遍历目录 [%s] 失败：%w", dir, err)
	}
	return nil
}
func deleteDirContents(dir string, expireTime time.Time) error {
	err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			slog.Error("访问路径失败", "path", path, "err", err)
			return nil // 跳过错误路径，继续处理
		}

		// 只删除文件，跳过目录
		if !d.IsDir() {
			// 获取文件信息（包含修改时间）
			fileInfo, err := d.Info()
			if err != nil {
				slog.Error("获取文件失败", "path", path, "err", err)
				return nil
			}
			// 判断文件是否过期
			if fileInfo.ModTime().Before(expireTime) {
				// 删除过期文件
				if errs := os.Remove(path); errs != nil {
					slog.Error("删除文件失败", "path", path, "err", err)
				} else {
					slog.Info("成功删除文件", "path", path)
				}
			}
		}
		return nil
	})

	if err != nil {
		return fmt.Errorf("遍历目录 [%s] 失败：%w", dir, err)
	}
	return nil
}

// cleanExpiredFiles 清理指定目录下超过expireDays天未修改的文件
func (c Core) cleanExpiredFiles() error {
	// 计算过期时间阈值
	expireDays := time.Duration(c.Cfg.VqdConfig.SaveDay)
	if expireDays < 1 {
		expireDays = 1
	}
	expireTime := time.Now().Add(-expireDays * 24 * time.Hour)
	slog.Info(fmt.Sprintf("开始清理目录 [%s] 中超过 %d 天的文件，过期时间阈值：%s", vqdcms.VQD_IMAGES_DIR, expireDays, expireTime.Format(time.RFC3339)))

	dir, _ := os.Getwd()
	rootDir := filepath.Join(dir, vqdcms.VQD_IMAGES_DIR)
	dateDirs, err := os.ReadDir(rootDir)
	if err != nil {
		slog.Error("访问根目录路径失败", "path", rootDir, "err", err)
		return nil
	}
	for _, d := range dateDirs {
		// 只处理目录（跳过文件）
		if !d.IsDir() {
			return nil
		}
		path := filepath.Join(rootDir, d.Name())
		if err := deleteTaskDirContents(path, expireTime); err != nil {
			slog.Error("清理目录内容失败", "path", path, "err", err)
			return nil
		}
		// 删除空的日期目录
		if err := os.Remove(path); err != nil {
			slog.Error("删除空目录失败（可能非空）", "path", path, "err", err)
		} else {
			slog.Info("成功删除过期目录", "path", path)
		}
	}

	slog.Info("本次文件清理任务执行完成")
	return nil
}
