package utils

import (
	"agent/commons/debug"
	"errors"
	"io/fs"
	"os"
	"path/filepath"
	"strings"
)

func DirIsExist(path string) bool {
	exist, _ := dirIsExist(path)
	return exist
}

func DirCreate(path string) error {
	if ok, err := dirIsExist(path); err != nil {
		return err
	} else if ok {
		return nil
	}
	return os.MkdirAll(path, os.ModePerm)
}

func dirIsExist(path string) (bool, error) {
	if info, err := os.Stat(path); err == nil {
		return info.IsDir(), nil
	} else if errors.Is(err, os.ErrNotExist) {
		return false, nil
	} else {
		return false, err
	}
}

func DirTraverse(path string, deep int, fn func(entry fs.DirEntry, path string, count int, deep int) error) (err error) {
	if path, err = pathAbs(path); err != nil {
		return err
	}
	return dirTraverse(nil, path, 0, deep, fn)
}

func dirTraverse(
	root fs.DirEntry, rootPath string, deep int, maxDeep int,
	fn func(entry fs.DirEntry, path string, count int, deep int) error,
) (err error) {
	// count > 0, папка содержит заданное количество элементов
	// count = 0, папка пустая
	// count = -1, отсутствует информация о содержимом папки

	if (maxDeep >= 0 && maxDeep < deep) || fn == nil {
		return nil
	}
	if root == nil {
		fileInfo, err := os.Stat(rootPath)
		if err != nil {
			return err
		}
		root = fs.FileInfoToDirEntry(fileInfo)
	}
	if !root.IsDir() {
		return fn(root, rootPath, 0, deep)
	}
	items, err := os.ReadDir(rootPath)
	if err != nil {
		return err
	}
	if err = fn(root, rootPath, len(items), deep); err != nil {
		return err
	}
	for _, entry := range items {
		path := filepath.Join(rootPath, entry.Name())
		if !entry.IsDir() {
			if err = fn(entry, path, 0, deep); err != nil {
				return err
			}
			continue
		}
		if maxDeep < deep+1 {
			if err = fn(entry, path, -1, deep); err != nil {
				return err
			}
			continue
		}
		if err = dirTraverse(entry, path, deep+1, maxDeep, fn); err != nil {
			debug.Log(err)
		}
	}
	return nil
}

func DirPathWalk(path string, fn func(entry fs.DirEntry, path string, count int, depth int) error) (err error) {
	if path, err = pathAbs(path); err != nil {
		return err
	}
	return dirPathWalk(path, fn)
}

func dirPathWalk(path string, fn func(entry fs.DirEntry, path string, count int, depth int) error) error {
	// Разбиваем путь на части
	parts := strings.Split(filepath.ToSlash(path), "/")
	if len(parts) > 1 && parts[len(parts)-1] == "" {
		// Удаляем последний пустой элемент, если путь заканчивался на '/'
		parts = parts[:len(parts)-1]
	}

	var currentPath string
	for depth := 0; depth < len(parts); depth++ {
		if depth == 0 {
			currentPath = parts[0] + string(os.PathSeparator)
		} else {
			currentPath = filepath.Join(currentPath, parts[depth])
		}

		entries, err := os.ReadDir(currentPath)
		if err != nil {
			return err
		}

		// Создаём фейковый entry для текущей папки
		dirInfo, err := os.Stat(currentPath)
		if err != nil {
			return err
		}
		dirEntry := fs.FileInfoToDirEntry(dirInfo)
		if fn != nil {
			if err := fn(dirEntry, currentPath, len(entries), depth); err != nil {
				return err
			}
		}

		for _, entry := range entries {
			entryPath := filepath.Join(currentPath, entry.Name())
			if fn != nil {
				count := 0
				if entry.IsDir() {
					count = -1
				}
				if err := fn(entry, entryPath, count, depth+1); err != nil {
					return err
				}
			}
		}
	}

	return nil
}
