package zip

import (
	"bytes"
	"io"
	"io/ioutil"
	"os"
	"strings"

	"github.com/pkg/errors"
	archive "github.com/tj/go-archive"
)

var transform = archive.TransformFunc(func(r io.Reader, i os.FileInfo) (io.Reader, os.FileInfo) {
	name := strings.Replace(i.Name(), "\\", "/", -1)

	i = archive.Info{
		Name:     name,
		Size:     i.Size(),
		Mode:     i.Mode() | 0555,
		Modified: i.ModTime(),
		Dir:      i.IsDir(),
	}.FileInfo()

	return r, i
})

// Build the given `dir`.
func Build(dir string) (io.ReadCloser, *archive.Stats, error) {
	upignore, err := read(".upignore")
	if err != nil {
		return nil, nil, errors.Wrap(err, "reading .upignore")
	}
	defer upignore.Close()

	r := io.MultiReader(
		strings.NewReader(".*\n"),
		strings.NewReader("\n!vendor\n!node_modules/**\n!.pypath/**\n"),
		upignore,
		strings.NewReader("\n!main\n!server\n!_proxy.js\n!up.json\n!pom.xml\n!build.gradle\n!project.clj\ngin-bin\nup\n"))

	filter, err := archive.FilterPatterns(r)
	if err != nil {
		return nil, nil, errors.Wrap(err, "parsing ignore patterns")
	}

	buf := new(bytes.Buffer)
	zip := archive.NewZip(buf).
		WithFilter(filter).
		WithTransform(transform)

	if err := zip.Open(); err != nil {
		return nil, nil, errors.Wrap(err, "opening")
	}

	if err := zip.AddDir(dir); err != nil {
		return nil, nil, errors.Wrap(err, "adding dir")
	}

	if err := zip.Close(); err != nil {
		return nil, nil, errors.Wrap(err, "closing")
	}

	return ioutil.NopCloser(buf), zip.Stats(), nil
}

// read file.
func read(path string) (io.ReadCloser, error) {
	f, err := os.Open(path)

	if os.IsNotExist(err) {
		return ioutil.NopCloser(bytes.NewReader(nil)), nil
	}

	if err != nil {
		return nil, err
	}

	return f, nil
}
