package zipx

import (
	"archive/zip"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"

	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
)

func Unpacking(name, destPath string, mapper func(f *zip.File) bool) error {
	r, err := zip.OpenReader(name)
	if err != nil {
		return err
	}
	defer r.Close()

	destAbsPath, err := filepath.Abs(destPath)
	if err != nil {
		return err
	}

	for _, file := range r.File {
		ok := mapper(file)
		if ok {
			err = fileCopy(file, destAbsPath)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

func fileCopy(file *zip.File, destPath string) error {
	rc, err := file.Open()
	if err != nil {
		return err
	}
	defer rc.Close()

	// Ensure the file path does not contain directory traversal elements
	if strings.Contains(file.Name, "..") {
		return fmt.Errorf("invalid file path: %s", file.Name)
	}

	abs, err := filepath.Abs(file.Name)
	if err != nil {
		return err
	}

	filename := filepath.Join(destPath, filepath.Base(abs))
	dir := filepath.Dir(filename)
	err = pathx.MkdirIfNotExist(dir)
	if err != nil {
		return err
	}

	w, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer w.Close()

	_, err = io.Copy(w, rc)
	if err != nil {
		return err
	}

	if file.Mode().IsRegular() && file.Mode()&0111 != 0 {
		return w.Chmod(file.Mode())
	}

	return nil
}
