package utils

import (
	"archive/zip"
	"errors"
	"io"
	"os"
	"strings"
)

type Unzip struct {
	zipFile     string
	targetDir   string
	stripPrefix string
}

func NewUnzip(zipFile string, targetDir string, stripPrefix string) *Unzip {
	return &Unzip{
		zipFile:     zipFile,
		targetDir:   targetDir,
		stripPrefix: stripPrefix,
	}
}

func (this *Unzip) Run() error {
	if len(this.zipFile) == 0 {
		return errors.New("zip file should not be empty")
	}
	if len(this.targetDir) == 0 {
		return errors.New("target dir should not be empty")
	}

	reader, err := zip.OpenReader(this.zipFile)
	if err != nil {
		return err
	}

	defer func() {
		_ = reader.Close()
	}()

	for _, file := range reader.File {
		info := file.FileInfo()
		filename := file.Name
		if len(this.stripPrefix) > 0 {
			filename = strings.TrimPrefix(filename, this.stripPrefix)
		}
		target := this.targetDir + "/" + filename

		// 目录
		if info.IsDir() {
			stat, err := os.Stat(target)
			if err != nil {
				if !os.IsNotExist(err) {
					return err
				} else {
					err = os.MkdirAll(target, info.Mode())
					if err != nil {
						return err
					}
				}
			} else if !stat.IsDir() {
				err = os.MkdirAll(target, info.Mode())
				if err != nil {
					return err
				}
			}
			continue
		}

		// 文件
		err := func(file *zip.File, target string) error {
			fileReader, err := file.Open()
			if err != nil {
				return err
			}
			defer func() {
				_ = fileReader.Close()
			}()

			fileWriter, err := os.OpenFile(target, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, file.FileInfo().Mode())
			if err != nil {
				return err
			}
			defer func() {
				_ = fileWriter.Close()
			}()

			_, err = io.Copy(fileWriter, fileReader)
			return err
		}(file, target)
		if err != nil {
			return err
		}
	}

	return nil
}
