// Version helps plugin creators set and track the plugin version using the same
// convenience functions used by the Packer core.
package version

import (
	"bytes"
	"fmt"

	"github.com/hashicorp/go-version"
)

// The git commit that was compiled. This will be filled in by the compiler.
var GitCommit string

// InitializePluginVersion initializes the SemVer and returns a version var.
// If the provided "version" string is not valid, the call to version.Must
// will panic. Therefore, this function should always be called in a package
// init() function to make sure that plugins are following proper semantic
// versioning and to make sure that plugins which aren't following proper
// semantic versioning crash immediately rather than later.
func InitializePluginVersion(vers, versionPrerelease string) *PluginVersion {
	pv := PluginVersion{
		version:           vers,
		versionPrerelease: versionPrerelease,
	}
	// This call initializes the SemVer to make sure that if Packer crashes due
	// to an invalid SemVer it's at the very beginning of the Packer run.
	pv.semVer = version.Must(version.NewVersion(vers))
	return &pv
}

type PluginVersion struct {
	// The main version number that is being run at the moment.
	version string
	// A pre-release marker for the version. If this is "" (empty string)
	// then it means that it is a final release. Otherwise, this is a pre-release
	// such as "dev" (in development), "beta", "rc1", etc.
	versionPrerelease string
	// The Semantic Version of the plugin. Used for version constraint comparisons
	semVer *version.Version
}

func (p *PluginVersion) FormattedVersion() string {
	var versionString bytes.Buffer
	fmt.Fprintf(&versionString, "%s", p.version)
	if p.versionPrerelease != "" {
		fmt.Fprintf(&versionString, "-%s", p.versionPrerelease)

		if GitCommit != "" {
			fmt.Fprintf(&versionString, " (%s)", GitCommit)
		}
	}

	return versionString.String()
}

func (p *PluginVersion) SemVer() *version.Version {
	if p.semVer != nil {
		// SemVer is an instance of version.Version. This has the secondary
		// benefit of verifying during tests and init time that our version is a
		// proper semantic version, which should always be the case.
		p.semVer = version.Must(version.NewVersion(p.version))
	}
	return p.semVer
}

func (p *PluginVersion) GetVersion() string {
	return p.version
}

func (p *PluginVersion) GetVersionPrerelease() string {
	return p.versionPrerelease
}

// String returns the complete version string, including prerelease
func (p *PluginVersion) String() string {
	if p.versionPrerelease != "" {
		return fmt.Sprintf("%s-%s", p.version, p.versionPrerelease)
	}
	return p.version
}
