package utils

import (
	"agent/defines/derrs"
	"errors"
	"io"
	"os"
	"path/filepath"
	"strings"
)

func FileCreate(filepath string, data ...[]byte) (err error) {
	fd, err := os.Create(filepath)
	if err != nil {
		return err
	}
	defer fd.Close()
	if len(data) > 0 {
		for _, d := range data {
			if _, err = fd.Write(d); err != nil && err != io.EOF {
				return err
			}
		}
	}
	return nil
}

func FileCreateFromReader(filepath string, source io.Reader) (size int64, err error) {
	fd, err := os.Create(filepath)
	if err != nil {
		return 0, err
	}
	defer fd.Close()
	return io.Copy(fd, source)
}

func FileIsExist(filepath string) bool {
	if stat, err := os.Stat(filepath); err == nil {
		if stat.IsDir() {
			return false
		}
		return true
	} else if errors.Is(err, os.ErrNotExist) {
		return false
	} else {
		return false
	}
}

func FileSize(filepath string) int64 {
	if len(filepath) == 0 {
		return 0
	}
	fd, err := os.Stat(filepath)
	if err != nil {
		return 0
	}
	return fd.Size()
}

func FileName(path string) string {
	filename := FileNameFull(path)
	if len(filename) == 0 {
		return ""
	}
	return filename[:len(filename)-len(filepath.Ext(filename))]
}

func FileExtension(path string) string {
	return filepath.Ext(FileNameFull(path))
}

func FileNameFull(path string) string {
	if path = filepath.Base(path); path == "." || path == "/" || path == "\\" {
		return ""
	}
	return path
}

func FileCopy(toFile string, fromFile string) (int64, error) {
	if n, err := fileCopyWithBuffSize(toFile, fromFile, 8096); err != nil {
		return 0, err
	} else {
		return n, nil
	}
}

func fileCopyWithBuffSize(toFile string, fromFile string, buffSize int64) (size int64, err error) {
	fromStat, err := os.Stat(fromFile)
	if err != nil {
		return 0, err
	}

	if !fromStat.Mode().IsRegular() {
		return 0, derrs.NewUnknownError("source file is not regular")
	}

	fromFd, err := os.Open(fromFile)
	if err != nil {
		return 0, err
	}
	defer fromFd.Close()

	toFd, err := os.OpenFile(toFile, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, fromStat.Mode())
	if err != nil {
		return 0, err
	}
	defer toFd.Close()

	buf := make([]byte, buffSize)
	for {
		n, err := fromFd.Read(buf)
		if err != nil && err != io.EOF {
			return 0, err
		}
		if n == 0 {
			break
		}

		if _, err := toFd.Write(buf[:n]); err != nil {
			return 0, err
		}
		size += int64(n)
	}

	if err = toFd.Sync(); err != nil {
		return size, err
	}

	return size, nil
}

func pathAbs(path string) (string, error) {
	// Для Windows, для дисков "С:", "D:" и т.д. нужно добавлять слеш в конце пути, иначе filepath.Clean сделает их
	// некорректными "C:.", "D:." и т.д.
	if strings.HasSuffix(path, ":") {
		path += string(os.PathSeparator)
	}
	path = filepath.Clean(path)
	if path == "" {
		return "", derrs.NewIncorrectParamsError("path is empty")
	}
	return filepath.Abs(path)
}
