package service

import (
	"fmt"
	"os"
	"path"
	"sort"
	"strings"
	"time"

	"github.com/1Panel-dev/1Panel/backend/app/dto"
	"github.com/1Panel-dev/1Panel/backend/global"
	"github.com/1Panel-dev/1Panel/backend/utils/cmd"
	"github.com/1Panel-dev/1Panel/backend/utils/common"
	fileUtils "github.com/1Panel-dev/1Panel/backend/utils/files"
	"github.com/google/uuid"
)

const (
	upgradePath      = "1panel/tmp/upgrade"
	snapshotTmpPath  = "1panel/tmp/system"
	rollbackPath     = "1panel/tmp"
	cachePath        = "1panel/cache"
	oldOriginalPath  = "original"
	oldAppBackupPath = "1panel/resource/apps_bak"
	oldDownloadPath  = "1panel/tmp/download"
	oldUpgradePath   = "1panel/tmp"
	tmpUploadPath    = "1panel/tmp/upload"
	uploadPath       = "1panel/uploads"
	downloadPath     = "1panel/download"
	logPath          = "1panel/log"
	dockerLogPath    = "1panel/tmp/docker_logs"
	taskPath         = "1panel/task"
)

func (u *DeviceService) Scan() dto.CleanData {
	var (
		SystemClean dto.CleanData
		treeData    []dto.CleanTree
	)
	fileOp := fileUtils.NewFileOp()

	originalPath := path.Join(global.CONF.System.BaseDir, "1panel_original")
	originalSize, _ := fileOp.GetDirSize(originalPath)
	treeData = append(treeData, dto.CleanTree{
		ID:          uuid.NewString(),
		Label:       "1panel_original",
		Size:        uint64(originalSize),
		IsCheck:     true,
		IsRecommend: true,
		Type:        "1panel_original",
		Children:    loadTreeWithDir(true, "1panel_original", originalPath, fileOp),
	})

	upgradePath := path.Join(global.CONF.System.BaseDir, upgradePath)
	upgradeSize, _ := fileOp.GetDirSize(upgradePath)
	treeData = append(treeData, dto.CleanTree{
		ID:          uuid.NewString(),
		Label:       "upgrade",
		Size:        uint64(upgradeSize),
		IsCheck:     false,
		IsRecommend: true,
		Type:        "upgrade",
		Children:    loadTreeWithDir(true, "upgrade", upgradePath, fileOp),
	})

	snapTree := loadSnapshotTree(fileOp)
	snapSize := uint64(0)
	for _, snap := range snapTree {
		snapSize += uint64(snap.Size)
	}
	treeData = append(treeData, dto.CleanTree{
		ID:          uuid.NewString(),
		Label:       "snapshot",
		Size:        snapSize,
		IsCheck:     true,
		IsRecommend: true,
		Type:        "snapshot",
		Children:    snapTree,
	})

	rollBackTree := loadRollBackTree(fileOp)
	rollbackSize := uint64(0)
	for _, rollback := range rollBackTree {
		rollbackSize += uint64(rollback.Size)
	}
	treeData = append(treeData, dto.CleanTree{
		ID:          uuid.NewString(),
		Label:       "rollback",
		Size:        rollbackSize,
		IsCheck:     true,
		IsRecommend: true,
		Type:        "rollback",
		Children:    rollBackTree,
	})

	cachePath := path.Join(global.CONF.System.BaseDir, cachePath)
	cacheSize, _ := fileOp.GetDirSize(cachePath)
	treeData = append(treeData, dto.CleanTree{
		ID:          uuid.NewString(),
		Label:       "cache",
		Size:        uint64(cacheSize),
		IsCheck:     false,
		IsRecommend: false,
		Type:        "cache",
	})

	unusedTree := loadUnusedFile(fileOp)
	unusedSize := uint64(0)
	for _, unused := range unusedTree {
		unusedSize += uint64(unused.Size)
	}
	treeData = append(treeData, dto.CleanTree{
		ID:          uuid.NewString(),
		Label:       "unused",
		Size:        unusedSize,
		IsCheck:     true,
		IsRecommend: true,
		Type:        "unused",
		Children:    unusedTree,
	})
	SystemClean.SystemClean = treeData

	uploadTreeData := loadUploadTree(fileOp)
	SystemClean.UploadClean = append(SystemClean.UploadClean, uploadTreeData...)

	downloadTreeData := loadDownloadTree(fileOp)
	SystemClean.DownloadClean = append(SystemClean.DownloadClean, downloadTreeData...)

	logTree := loadLogTree(fileOp)
	SystemClean.SystemLogClean = append(SystemClean.SystemLogClean, logTree...)

	return SystemClean
}

func (u *DeviceService) Clean(req []dto.Clean) {
	size := uint64(0)
	restart := false
	for _, item := range req {
		size += item.Size
		switch item.TreeType {
		case "1panel_original":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, "1panel_original", item.Name))

		case "upgrade":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, upgradePath, item.Name))

		case "snapshot":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, snapshotTmpPath, item.Name))
			dropFileOrDir(path.Join(global.CONF.System.Backup, "system", item.Name))
		case "snapshot_tmp":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, snapshotTmpPath, item.Name))
		case "snapshot_local":
			dropFileOrDir(path.Join(global.CONF.System.Backup, "system", item.Name))

		case "rollback":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, rollbackPath, "app"))
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, rollbackPath, "database"))
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, rollbackPath, "website"))
		case "rollback_app":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, rollbackPath, "app", item.Name))
		case "rollback_database":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, rollbackPath, "database", item.Name))
		case "rollback_website":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, rollbackPath, "website", item.Name))

		case "cache":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, cachePath, item.Name))
			restart = true

		case "unused":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, oldOriginalPath))
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, oldAppBackupPath))
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, oldDownloadPath))
			files, _ := os.ReadDir(path.Join(global.CONF.System.BaseDir, oldUpgradePath))
			if len(files) == 0 {
				continue
			}
			for _, file := range files {
				if strings.HasPrefix(file.Name(), "upgrade_") {
					dropFileOrDir(path.Join(global.CONF.System.BaseDir, oldUpgradePath, file.Name()))
				}
			}
		case "old_original":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, oldOriginalPath, item.Name))
		case "old_apps_bak":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, oldAppBackupPath, item.Name))
		case "old_download":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, oldDownloadPath, item.Name))
		case "old_upgrade":
			if len(item.Name) == 0 {
				files, _ := os.ReadDir(path.Join(global.CONF.System.BaseDir, oldUpgradePath))
				if len(files) == 0 {
					continue
				}
				for _, file := range files {
					if strings.HasPrefix(file.Name(), "upgrade_") {
						dropFileOrDir(path.Join(global.CONF.System.BaseDir, oldUpgradePath, file.Name()))
					}
				}
			} else {
				dropFileOrDir(path.Join(global.CONF.System.BaseDir, oldUpgradePath, item.Name))
			}

		case "upload":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, uploadPath, item.Name))
			if len(item.Name) == 0 {
				dropFileOrDir(path.Join(global.CONF.System.BaseDir, tmpUploadPath))
			}
		case "upload_tmp":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, tmpUploadPath, item.Name))
		case "upload_app":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, uploadPath, "app", item.Name))
		case "upload_database":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, uploadPath, "database", item.Name))
		case "upload_website":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, uploadPath, "website", item.Name))
		case "upload_directory":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, uploadPath, "directory", item.Name))
		case "download":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, downloadPath, item.Name))
		case "download_app":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, downloadPath, "app", item.Name))
		case "download_database":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, downloadPath, "database", item.Name))
		case "download_website":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, downloadPath, "website", item.Name))
		case "download_directory":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, downloadPath, "directory", item.Name))

		case "system_log":
			if len(item.Name) == 0 {
				files, _ := os.ReadDir(path.Join(global.CONF.System.BaseDir, logPath))
				if len(files) == 0 {
					continue
				}
				for _, file := range files {
					if file.Name() == "1Panel.log" || file.IsDir() {
						continue
					}
					dropFileOrDir(path.Join(global.CONF.System.BaseDir, logPath, file.Name()))
				}
			} else {
				dropFileOrDir(path.Join(global.CONF.System.BaseDir, logPath, item.Name))
			}
		case "docker_log":
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, dockerLogPath, item.Name))
		case "task_log":
			pathItem := path.Join(global.CONF.System.BaseDir, taskPath, item.Name)
			dropFileOrDir(path.Join(global.CONF.System.BaseDir, taskPath, item.Name))
			if len(item.Name) == 0 {
				files, _ := os.ReadDir(pathItem)
				if len(files) == 0 {
					continue
				}
				for _, file := range files {
					_ = cronjobRepo.DeleteRecord(cronjobRepo.WithByRecordFile(path.Join(pathItem, file.Name())))
				}
			} else {
				_ = cronjobRepo.DeleteRecord(cronjobRepo.WithByRecordFile(pathItem))
			}
		}
	}

	_ = settingRepo.Update("LastCleanTime", time.Now().Format("2006-01-02 15:04:05"))
	_ = settingRepo.Update("LastCleanSize", fmt.Sprintf("%v", size))
	_ = settingRepo.Update("LastCleanData", fmt.Sprintf("%v", len(req)))

	if restart {
		go func() {
			_, err := cmd.Exec("systemctl restart 1panel.service")
			if err != nil {
				global.LOG.Errorf("restart system port failed, err: %v", err)
			}
		}()
	}
}

func (u *DeviceService) CleanForCronjob() (string, error) {
	logs := ""
	size := int64(0)
	fileCount := 0
	dropFileOrDirWithLog(path.Join(global.CONF.System.BaseDir, "1panel_original"), &logs, &size, &fileCount)

	upgradePath := path.Join(global.CONF.System.BaseDir, upgradePath)
	upgradeFiles, _ := os.ReadDir(upgradePath)
	if len(upgradeFiles) != 0 {
		sort.Slice(upgradeFiles, func(i, j int) bool {
			return upgradeFiles[i].Name() > upgradeFiles[j].Name()
		})
		for i := 0; i < len(upgradeFiles); i++ {
			if i != 0 {
				dropFileOrDirWithLog(path.Join(upgradePath, upgradeFiles[i].Name()), &logs, &size, &fileCount)
			}
		}
	}

	dropFileOrDirWithLog(path.Join(global.CONF.System.BaseDir, snapshotTmpPath), &logs, &size, &fileCount)
	dropFileOrDirWithLog(path.Join(global.CONF.System.Backup, "system"), &logs, &size, &fileCount)

	dropFileOrDirWithLog(path.Join(global.CONF.System.BaseDir, rollbackPath, "app"), &logs, &size, &fileCount)
	dropFileOrDirWithLog(path.Join(global.CONF.System.BaseDir, rollbackPath, "website"), &logs, &size, &fileCount)
	dropFileOrDirWithLog(path.Join(global.CONF.System.BaseDir, rollbackPath, "database"), &logs, &size, &fileCount)

	dropFileOrDirWithLog(path.Join(global.CONF.System.BaseDir, oldOriginalPath), &logs, &size, &fileCount)
	dropFileOrDirWithLog(path.Join(global.CONF.System.BaseDir, oldAppBackupPath), &logs, &size, &fileCount)
	dropFileOrDirWithLog(path.Join(global.CONF.System.BaseDir, oldDownloadPath), &logs, &size, &fileCount)
	oldUpgradePath := path.Join(global.CONF.System.BaseDir, oldUpgradePath)
	oldUpgradeFiles, _ := os.ReadDir(oldUpgradePath)
	if len(oldUpgradeFiles) != 0 {
		for i := 0; i < len(oldUpgradeFiles); i++ {
			dropFileOrDirWithLog(path.Join(oldUpgradePath, oldUpgradeFiles[i].Name()), &logs, &size, &fileCount)
		}
	}

	dropFileOrDirWithLog(path.Join(global.CONF.System.BaseDir, tmpUploadPath), &logs, &size, &fileCount)
	dropFileOrDirWithLog(path.Join(global.CONF.System.BaseDir, uploadPath), &logs, &size, &fileCount)
	dropFileOrDirWithLog(path.Join(global.CONF.System.BaseDir, downloadPath), &logs, &size, &fileCount)

	logPath := path.Join(global.CONF.System.BaseDir, logPath)
	logFiles, _ := os.ReadDir(logPath)
	if len(logFiles) != 0 {
		for i := 0; i < len(logFiles); i++ {
			if logFiles[i].Name() != "1Panel.log" {
				dropFileOrDirWithLog(path.Join(logPath, logFiles[i].Name()), &logs, &size, &fileCount)
			}
		}
	}
	timeNow := time.Now().Format("2006-01-02 15:04:05")
	dropFileOrDirWithLog(path.Join(global.CONF.System.BaseDir, dockerLogPath), &logs, &size, &fileCount)
	logs += fmt.Sprintf("\n%s: total clean: %s, total count: %d", timeNow, common.LoadSizeUnit2F(float64(size)), fileCount)

	_ = settingRepo.Update("LastCleanTime", timeNow)
	_ = settingRepo.Update("LastCleanSize", fmt.Sprintf("%v", size))
	_ = settingRepo.Update("LastCleanData", fmt.Sprintf("%v", fileCount))

	return logs, nil
}

func loadSnapshotTree(fileOp fileUtils.FileOp) []dto.CleanTree {
	var treeData []dto.CleanTree
	path1 := path.Join(global.CONF.System.BaseDir, snapshotTmpPath)
	list1 := loadTreeWithAllFile(true, path1, "snapshot_tmp", path1, fileOp)
	if len(list1) != 0 {
		size, _ := fileOp.GetDirSize(path1)
		treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "snapshot_tmp", Size: uint64(size), Children: list1, Type: "snapshot_tmp", IsRecommend: true})
	}

	path2 := path.Join(global.CONF.System.Backup, "system")
	list2 := loadTreeWithAllFile(true, path2, "snapshot_local", path2, fileOp)
	if len(list2) != 0 {
		size, _ := fileOp.GetDirSize(path2)
		treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "snapshot_local", Size: uint64(size), Children: list2, Type: "snapshot_local", IsRecommend: true})
	}
	return treeData
}

func loadRollBackTree(fileOp fileUtils.FileOp) []dto.CleanTree {
	var treeData []dto.CleanTree
	path1 := path.Join(global.CONF.System.BaseDir, rollbackPath, "app")
	list1 := loadTreeWithAllFile(true, path1, "rollback_app", path1, fileOp)
	size1, _ := fileOp.GetDirSize(path1)
	treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "rollback_app", Size: uint64(size1), Children: list1, Type: "rollback_app", IsRecommend: true})

	path2 := path.Join(global.CONF.System.BaseDir, rollbackPath, "website")
	list2 := loadTreeWithAllFile(true, path2, "rollback_website", path2, fileOp)
	size2, _ := fileOp.GetDirSize(path2)
	treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "rollback_website", Size: uint64(size2), Children: list2, Type: "rollback_website", IsRecommend: true})

	path3 := path.Join(global.CONF.System.BaseDir, rollbackPath, "database")
	list3 := loadTreeWithAllFile(true, path3, "rollback_database", path3, fileOp)
	size3, _ := fileOp.GetDirSize(path3)
	treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "rollback_database", Size: uint64(size3), Children: list3, Type: "rollback_database", IsRecommend: true})

	return treeData
}

func loadUnusedFile(fileOp fileUtils.FileOp) []dto.CleanTree {
	var treeData []dto.CleanTree
	path1 := path.Join(global.CONF.System.BaseDir, oldOriginalPath)
	list1 := loadTreeWithAllFile(true, path1, "old_original", path1, fileOp)
	if len(list1) != 0 {
		size, _ := fileOp.GetDirSize(path1)
		treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "old_original", Size: uint64(size), Children: list1, Type: "old_original"})
	}

	path2 := path.Join(global.CONF.System.BaseDir, oldAppBackupPath)
	list2 := loadTreeWithAllFile(true, path2, "old_apps_bak", path2, fileOp)
	if len(list2) != 0 {
		size, _ := fileOp.GetDirSize(path2)
		treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "old_apps_bak", Size: uint64(size), Children: list2, Type: "old_apps_bak"})
	}

	path3 := path.Join(global.CONF.System.BaseDir, oldDownloadPath)
	list3 := loadTreeWithAllFile(true, path3, "old_download", path3, fileOp)
	if len(list3) != 0 {
		size, _ := fileOp.GetDirSize(path3)
		treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "old_download", Size: uint64(size), Children: list3, Type: "old_download"})
	}

	path4 := path.Join(global.CONF.System.BaseDir, oldUpgradePath)
	list4 := loadTreeWithDir(true, "old_upgrade", path4, fileOp)
	itemSize := uint64(0)
	for _, item := range list4 {
		itemSize += item.Size
	}
	if len(list4) != 0 {
		treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "old_upgrade", Size: itemSize, Children: list4, Type: "old_upgrade"})
	}
	return treeData
}

func loadUploadTree(fileOp fileUtils.FileOp) []dto.CleanTree {
	var treeData []dto.CleanTree

	path0 := path.Join(global.CONF.System.BaseDir, tmpUploadPath)
	list0 := loadTreeWithAllFile(true, path0, "upload_tmp", path0, fileOp)
	size0, _ := fileOp.GetDirSize(path0)
	treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "upload_tmp", Size: uint64(size0), Children: list0, Type: "upload_tmp", IsRecommend: true})

	path1 := path.Join(global.CONF.System.BaseDir, uploadPath, "app")
	list1 := loadTreeWithAllFile(true, path1, "upload_app", path1, fileOp)
	size1, _ := fileOp.GetDirSize(path1)
	treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "upload_app", Size: uint64(size1), Children: list1, Type: "upload_app", IsRecommend: true})

	path2 := path.Join(global.CONF.System.BaseDir, uploadPath, "website")
	list2 := loadTreeWithAllFile(true, path2, "upload_website", path2, fileOp)
	size2, _ := fileOp.GetDirSize(path2)
	treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "upload_website", Size: uint64(size2), Children: list2, Type: "upload_website", IsRecommend: true})

	path3 := path.Join(global.CONF.System.BaseDir, uploadPath, "database")
	list3 := loadTreeWithAllFile(true, path3, "upload_database", path3, fileOp)
	size3, _ := fileOp.GetDirSize(path3)
	treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "upload_database", Size: uint64(size3), Children: list3, Type: "upload_database", IsRecommend: true})

	path4 := path.Join(global.CONF.System.BaseDir, uploadPath, "directory")
	list4 := loadTreeWithAllFile(true, path4, "upload_directory", path4, fileOp)
	size4, _ := fileOp.GetDirSize(path4)
	treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "upload_directory", Size: uint64(size4), Children: list4, Type: "upload_directory", IsRecommend: true})

	path5 := path.Join(global.CONF.System.BaseDir, uploadPath)
	uploadTreeData := loadTreeWithAllFile(true, path5, "upload", path5, fileOp)
	treeData = append(treeData, uploadTreeData...)

	return treeData
}

func loadDownloadTree(fileOp fileUtils.FileOp) []dto.CleanTree {
	var treeData []dto.CleanTree
	path1 := path.Join(global.CONF.System.BaseDir, downloadPath, "app")
	list1 := loadTreeWithAllFile(true, path1, "download_app", path1, fileOp)
	size1, _ := fileOp.GetDirSize(path1)
	treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "download_app", Size: uint64(size1), Children: list1, Type: "download_app", IsRecommend: true})

	path2 := path.Join(global.CONF.System.BaseDir, downloadPath, "website")
	list2 := loadTreeWithAllFile(true, path2, "download_website", path2, fileOp)
	size2, _ := fileOp.GetDirSize(path2)
	treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "download_website", Size: uint64(size2), Children: list2, Type: "download_website", IsRecommend: true})

	path3 := path.Join(global.CONF.System.BaseDir, downloadPath, "database")
	list3 := loadTreeWithAllFile(true, path3, "download_database", path3, fileOp)
	size3, _ := fileOp.GetDirSize(path3)
	treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "download_database", Size: uint64(size3), Children: list3, Type: "download_database", IsRecommend: true})

	path4 := path.Join(global.CONF.System.BaseDir, downloadPath, "directory")
	list4 := loadTreeWithAllFile(true, path4, "download_directory", path4, fileOp)
	size4, _ := fileOp.GetDirSize(path4)
	treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "download_directory", Size: uint64(size4), Children: list4, Type: "download_directory", IsRecommend: true})

	path5 := path.Join(global.CONF.System.BaseDir, downloadPath)
	uploadTreeData := loadTreeWithAllFile(true, path5, "download", path5, fileOp)
	treeData = append(treeData, uploadTreeData...)

	return treeData
}

func loadLogTree(fileOp fileUtils.FileOp) []dto.CleanTree {
	var treeData []dto.CleanTree
	path1 := path.Join(global.CONF.System.BaseDir, logPath)
	list1 := loadTreeWithAllFile(true, path1, "system_log", path1, fileOp)
	size := uint64(0)
	for _, file := range list1 {
		size += file.Size
	}
	treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "system_log", Size: uint64(size), Children: list1, Type: "system_log", IsRecommend: true})

	path2 := path.Join(global.CONF.System.BaseDir, dockerLogPath)
	list2 := loadTreeWithAllFile(true, path2, "docker_log", path2, fileOp)
	size2, _ := fileOp.GetDirSize(path2)
	treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "docker_log", Size: uint64(size2), Children: list2, Type: "docker_log", IsRecommend: true})

	path3 := path.Join(global.CONF.System.BaseDir, taskPath)
	list3 := loadTreeWithAllFile(false, path3, "task_log", path3, fileOp)
	size3, _ := fileOp.GetDirSize(path3)
	treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "task_log", Size: uint64(size3), Children: list3, Type: "task_log"})
	return treeData
}

func loadTreeWithDir(isCheck bool, treeType, pathItem string, fileOp fileUtils.FileOp) []dto.CleanTree {
	var lists []dto.CleanTree
	files, err := os.ReadDir(pathItem)
	if err != nil {
		return lists
	}
	sort.Slice(files, func(i, j int) bool {
		return files[i].Name() > files[j].Name()
	})
	for _, file := range files {
		if (treeType == "old_upgrade" || treeType == "upgrade") && !strings.HasPrefix(file.Name(), "upgrade_2023") {
			continue
		}
		if file.IsDir() {
			size, err := fileOp.GetDirSize(path.Join(pathItem, file.Name()))
			if err != nil {
				continue
			}
			item := dto.CleanTree{
				ID:          uuid.NewString(),
				Label:       file.Name(),
				Type:        treeType,
				Size:        uint64(size),
				Name:        strings.TrimPrefix(file.Name(), "/"),
				IsCheck:     isCheck,
				IsRecommend: isCheck,
			}
			if treeType == "upgrade" && len(lists) == 0 {
				item.IsCheck = false
				item.IsRecommend = false
			}
			lists = append(lists, item)
		}
	}
	return lists
}

func loadTreeWithAllFile(isCheck bool, originalPath, treeType, pathItem string, fileOp fileUtils.FileOp) []dto.CleanTree {
	var lists []dto.CleanTree

	files, err := os.ReadDir(pathItem)
	if err != nil {
		return lists
	}
	for _, file := range files {
		if treeType == "system_log" && (file.Name() == "1Panel.log" || file.IsDir()) {
			continue
		}
		if (treeType == "upload" || treeType == "download") && file.IsDir() && (file.Name() == "app" || file.Name() == "database" || file.Name() == "website" || file.Name() == "directory") {
			continue
		}
		size := uint64(0)
		name := strings.TrimPrefix(path.Join(pathItem, file.Name()), originalPath+"/")
		if file.IsDir() {
			sizeItem, err := fileOp.GetDirSize(path.Join(pathItem, file.Name()))
			if err != nil {
				continue
			}
			size = uint64(sizeItem)
		} else {
			fileInfo, err := file.Info()
			if err != nil {
				continue
			}
			size = uint64(fileInfo.Size())
		}
		item := dto.CleanTree{
			ID:          uuid.NewString(),
			Label:       file.Name(),
			Type:        treeType,
			Size:        uint64(size),
			Name:        name,
			IsCheck:     isCheck,
			IsRecommend: isCheck,
		}
		if file.IsDir() {
			item.Children = loadTreeWithAllFile(isCheck, originalPath, treeType, path.Join(pathItem, file.Name()), fileOp)
		}
		lists = append(lists, item)
	}
	return lists
}

func dropFileOrDir(itemPath string) {
	global.LOG.Debugf("drop file %s", itemPath)
	if err := os.RemoveAll(itemPath); err != nil {
		global.LOG.Errorf("drop file %s failed, err %v", itemPath, err)
	}
}

func dropFileOrDirWithLog(itemPath string, log *string, size *int64, count *int) {
	itemSize := int64(0)
	itemCount := 0
	scanFile(itemPath, &itemSize, &itemCount)
	*size += itemSize
	*count += itemCount
	if err := os.RemoveAll(itemPath); err != nil {
		global.LOG.Errorf("drop file %s failed, err %v", itemPath, err)
		*log += fmt.Sprintf("- drop file %s failed, err: %v \n\n", itemPath, err)
		return
	}
	*log += fmt.Sprintf("+ drop file %s successful!, size: %s, count: %d \n\n", itemPath, common.LoadSizeUnit2F(float64(itemSize)), itemCount)
}

func scanFile(pathItem string, size *int64, count *int) {
	files, _ := os.ReadDir(pathItem)
	for _, f := range files {
		if f.IsDir() {
			scanFile(path.Join(pathItem, f.Name()), size, count)
		} else {
			fileInfo, err := f.Info()
			if err != nil {
				continue
			}
			*count++
			*size += fileInfo.Size()
		}
	}
}