package util

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

	"github.com/google/uuid"
	"github.com/spf13/viper"
)

// TODO: can be private in the future.
const (
	// PluginExecutableName references the unzippped name of a plugin.
	PluginExecutableName string = "plugin"
	// ManifestFileName references the name of a manifest file that has been
	// requested while creating a new device of type plugin/csbi.
	ManifestFileName string = "plugin.yaml"
	// BundledPluginName references the name of a zipped plugin.
	BundledPluginName string = "bundled_plugin.zip"
)

func UnzipPlugin(id uuid.UUID) error {
	folderName := viper.GetString("plugin-folder")
	path := filepath.Clean(filepath.Join(folderName, id.String(), BundledPluginName))

	pluginArchive, err := zip.OpenReader(path)
	if err != nil {
		return err
	}
	defer func() {
		if ferr := pluginArchive.Close(); ferr != nil {
			err = ferr
		}
	}()

	for _, file := range pluginArchive.File {
		if err := extractFile(filepath.Dir(path), file); err != nil {
			return err
		}
	}

	if err := os.Remove(path); err != nil {
		return err
	}
	return nil
}

func extractFile(destination string, archivedFile *zip.File) error {
	archivedFileReader, err := archivedFile.Open()
	if err != nil {
		return err
	}
	defer func() {
		if err := archivedFileReader.Close(); err != nil {
			panic(err)
		}
	}()

	destinationFile, err := os.OpenFile(filepath.Join(destination, archivedFile.Name), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, archivedFile.Mode())
	if err != nil {
		return err
	}
	defer func() {
		if err := destinationFile.Close(); err != nil {
			panic(err)
		}
	}()

	_, err = io.Copy(destinationFile, archivedFileReader)
	if err != nil {
		return err
	}

	return nil
}
