package files

import (
	"compress/bzip2"
	"compress/gzip"
	"fmt"
	"godo/libs"
	"io"
	"os"
	"path/filepath"
	"strings"

	"archive/tar"
	"archive/zip"
)

// Decompress 解压文件到指定目录,支持多种压缩格式
func Decompress(compressedFilePath, destPath string) error {
	ext := filepath.Ext(compressedFilePath)
	switch ext {
	case ".zip":
		return Unzip(compressedFilePath, destPath)
	case ".tar":
		tarFile, err := os.Open(compressedFilePath)
		if err != nil {
			return err
		}
		defer tarFile.Close()
		return Untar(tarFile, destPath)
	case ".gz":
		return Untargz(compressedFilePath, destPath)
	case ".bz2":
		return Untarbz2(compressedFilePath, destPath)
	default:
		return fmt.Errorf("unsupported compression format: %s", ext)
	}
}

// Untar 解压.tar文件到指定目录
func Untar(reader io.Reader, destPath string) error {
	tarReader := tar.NewReader(reader)
	for {
		header, err := tarReader.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}

		target := filepath.Join(destPath, header.Name)

		// Ensure that all directories in the path are created.
		if header.Typeflag == tar.TypeDir {
			if !libs.PathExists(target) {
				if err := os.MkdirAll(target, os.FileMode(header.Mode)); err != nil {
					return err
				}
			}

			continue
		}

		// If it's a regular file, ensure its parent directories exist.
		if !libs.PathExists(target) {
			if err := os.MkdirAll(filepath.Dir(target), os.ModePerm); err != nil {
				return err
			}
		}

		// Create the file and write the contents from the tar archive.
		outFile, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR|os.O_TRUNC, os.FileMode(header.Mode))
		if err != nil {
			return err
		}
		defer outFile.Close()

		if _, err := io.Copy(outFile, tarReader); err != nil {
			return err
		}
	}
	return nil
}

// Untargz 解压.tar.gz文件到指定目录
func Untargz(targzFilePath, destPath string) error {
	gzFile, err := os.Open(targzFilePath)
	if err != nil {
		return err
	}
	defer gzFile.Close()

	gzReader, err := gzip.NewReader(gzFile)
	if err != nil {
		return err
	}
	defer gzReader.Close()

	return Untar(gzReader, destPath) // 传递gzReader给Untar
}

// Untarbz2 解压.tar.bz2文件到指定目录
func Untarbz2(tarbz2FilePath, destPath string) error {
	bz2File, err := os.Open(tarbz2FilePath)
	if err != nil {
		return err
	}
	defer bz2File.Close()

	bz2Reader := bzip2.NewReader(bz2File)
	return Untar(bz2Reader, destPath) // 传递bz2Reader给Untar
}

// DecompressZip 解压zip文件到指定目录
func Unzip(zipFilePath, destPath string) error {
	r, err := zip.OpenReader(zipFilePath)
	if err != nil {
		return err
	}
	defer r.Close()

	for _, f := range r.File {
		rc, err := f.Open()
		if err != nil {
			return err
		}
		defer rc.Close()

		path := filepath.Join(destPath, f.Name)

		// Ensure parent directory exists before creating the file or directory.
		if !libs.PathExists(filepath.Dir(path)) {
			if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
				return err
			}
		}

		if f.FileInfo().IsDir() {
			// Create the directory itself if it doesn't exist yet.
			if !libs.PathExists(path) {
				if err := os.Mkdir(path, f.Mode()); err != nil {
					return err
				}
			}

			continue
		}

		// Open the file for writing, truncating if it already exists.
		outFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
		if err != nil {
			return err
		}
		defer outFile.Close()

		// Write the contents of the zip file entry to the output file.
		if _, err := io.Copy(outFile, rc); err != nil {
			return err
		}
	}

	return nil
}

// IsSupportedCompressionFormat 判断文件后缀是否为支持的压缩格式
func IsSupportedCompressionFormat(filename string) bool {
	supportedFormats := map[string]bool{
		".zip": true,
		".tar": true,
		".gz":  true,
		".bz2": true,
	}

	for format := range supportedFormats {
		if strings.HasSuffix(strings.ToLower(filename), format) {
			return true
		}
	}
	return false
}

// HandlerFile 根据文件后缀判断是否支持解压,支持则解压,否则移动文件
func HandlerFile(filePath string, destDir string) error {
	// log.Printf("HandlerFile: %s", filePath)
	// log.Printf("destDir: %s", destDir)
	if IsSupportedCompressionFormat(filePath) {
		// 移动文件
		// newPath := filepath.Join(destDir, filepath.Base(filePath))
		// return os.Rename(filePath, newPath)
		// 解压文件
		return Decompress(filePath, destDir)
	}
	return nil

}