// MIT License
//
// Copyright (c) 2024 godoos.com
// Email: xpbb@qq.com
// GitHub: github.com/phpk/godoos
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
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) (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) (string, error) {
	var topLevelDirName string
	tarReader := tar.NewReader(reader)
	for {
		header, err := tarReader.Next()
		if err == io.EOF {
			break
		}
		if err != nil {
			return "", err
		}

		// Determine the top-level directory name
		if header.Typeflag == tar.TypeDir && topLevelDirName == "" {
			topLevelDirName = header.Name
		} else {
			topLevelDirName = destPath
		}

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

		if header.Typeflag == tar.TypeDir {
			if err := os.MkdirAll(target, os.FileMode(header.Mode)); err != nil {
				return "", err
			}
			continue
		}

		if err := os.MkdirAll(filepath.Dir(target), os.ModePerm); err != nil {
			return "", err
		}

		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 the full path of the top-level directory
	topLevelDestPath := filepath.Join(destPath, topLevelDirName)
	return topLevelDestPath, nil
}

// Untargz 解压.tar.gz文件到指定目录
func Untargz(targzFilePath, destPath string) (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)
}

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

	bz2Reader := bzip2.NewReader(bz2File)
	return Untar(bz2Reader, destPath)
}

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

	// 初始化顶层目录名称为空
	var topLevelDirName string
	var topLevelDestPath string
	// 遍历ZIP文件中的每个条目
	for _, f := range r.File {
		// 如果是目录且不是根目录,则更新顶层目录名称
		if f.FileInfo().IsDir() && f.Name != "" {
			// 去掉末尾的斜杠
			cleanedName := strings.TrimSuffix(f.Name, string(os.PathSeparator))
			if topLevelDirName == "" || cleanedName < topLevelDirName {
				topLevelDirName = cleanedName
			}
		}
	}

	// 检查是否找到了顶层目录
	if topLevelDirName == "" {
		topLevelDestPath = destPath
	} else {
		// 完整的顶层目录路径
		topLevelDestPath = filepath.Join(destPath, topLevelDirName)
	}

	// 再次遍历ZIP文件中的每个条目,这次进行解压操作
	for _, f := range r.File {
		rc, err := f.Open()
		if err != nil {
			return "", err
		}
		defer rc.Close()

		// 调整路径以确保所有文件都在顶层目录内
		//path := filepath.Join(topLevelDestPath, strings.TrimPrefix(f.Name, topLevelDirName+string(os.PathSeparator)))
		path := filepath.Join(topLevelDestPath, strings.TrimPrefix(f.Name, topLevelDirName))

		// 确保父目录存在
		if !libs.PathExists(filepath.Dir(path)) {
			if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
				return "", err
			}
		}

		if f.FileInfo().IsDir() {
			// 创建目录
			if !libs.PathExists(path) {
				if err := os.Mkdir(path, f.Mode()); err != nil {
					return "", err
				}
			}
		} else {
			// 创建并写入文件
			outFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
			if err != nil {
				return "", err
			}
			defer outFile.Close()

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

	// 返回完整的顶层目录路径
	return topLevelDestPath, 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) (string, error) {
	// log.Printf("HandlerFile: %s", filePath)
	// log.Printf("destDir: %s", destDir)
	if IsSupportedCompressionFormat(filePath) {
		return Decompress(filePath, destDir)
	} else {
		return "", fmt.Errorf("unsupported compression format: %s", filePath)
	}

}