package archive

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"encoding/json"
	"errors"
	"io"

	"github.com/open-policy-agent/opa/v1/util"
)

type TarGzWriter struct {
	*tar.Writer

	gw *gzip.Writer
}

func NewTarGzWriter(w io.Writer) *TarGzWriter {
	gw := gzip.NewWriter(w)
	tw := tar.NewWriter(gw)

	return &TarGzWriter{
		Writer: tw,
		gw:     gw,
	}
}

func (tgw *TarGzWriter) WriteFile(path string, bs []byte) (err error) {
	hdr := &tar.Header{
		Name:     path,
		Mode:     0600,
		Typeflag: tar.TypeReg,
		Size:     int64(len(bs)),
	}

	if err = tgw.WriteHeader(hdr); err == nil {
		_, err = tgw.Write(bs)
	}

	return err
}

func (tgw *TarGzWriter) WriteJSONFile(path string, v any) error {
	buf := &bytes.Buffer{}
	if err := json.NewEncoder(buf).Encode(v); err != nil {
		return err
	}

	return tgw.WriteFile(path, buf.Bytes())
}

func (tgw *TarGzWriter) Close() error {
	return errors.Join(tgw.Writer.Close(), tgw.gw.Close())
}

// MustWriteTarGz writes the list of file names and content into a tarball.
// Paths are prefixed with "/".
func MustWriteTarGz(files [][2]string) *bytes.Buffer {
	buf := &bytes.Buffer{}
	tgw := NewTarGzWriter(buf)
	defer tgw.Close()

	for _, file := range files {
		if err := tgw.WriteFile(util.WithPrefix(file[0], "/"), []byte(file[1])); err != nil {
			panic(err)
		}
	}

	return buf
}
