package command

import (
	"bytes"
	"fmt"
	"sort"
)

// VersionCommand is a Command implementation prints the version.
type VersionCommand struct {
	Meta

	Revision          string
	Version           string
	VersionPrerelease string
	CheckFunc         VersionCheckFunc
}

// VersionCheckFunc is the callback called by the Version command to
// check if there is a new version of Terraform.
type VersionCheckFunc func() (VersionCheckInfo, error)

// VersionCheckInfo is the return value for the VersionCheckFunc callback
// and tells the Version command information about the latest version
// of Terraform.
type VersionCheckInfo struct {
	Outdated bool
	Latest   string
	Alerts   []string
}

func (c *VersionCommand) Help() string {
	return ""
}

func (c *VersionCommand) Run(args []string) int {
	var versionString bytes.Buffer
	args, err := c.Meta.process(args, false)
	if err != nil {
		return 1
	}

	fmt.Fprintf(&versionString, "Terraform v%s", c.Version)
	if c.VersionPrerelease != "" {
		fmt.Fprintf(&versionString, "-%s", c.VersionPrerelease)

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

	c.Ui.Output(versionString.String())

	// We'll also attempt to print out the selected plugin versions. We can
	// do this only if "terraform init" was already run and thus we've committed
	// to a specific set of plugins. If not, the plugins lock will be empty
	// and so we'll show _no_ providers.
	//
	// Generally-speaking this is a best-effort thing that will give us a good
	// result in the usual case where the user successfully ran "terraform init"
	// and then hit a problem running _another_ command.
	providerPlugins := c.providerPluginSet()
	pluginsLockFile := c.providerPluginsLock()
	pluginsLock := pluginsLockFile.Read()
	var pluginVersions []string
	for meta := range providerPlugins {
		name := meta.Name
		wantHash, wanted := pluginsLock[name]
		if !wanted {
			// Ignore providers that aren't used by the current config at all
			continue
		}
		gotHash, err := meta.SHA256()
		if err != nil {
			// if we can't read the file to hash it, ignore it.
			continue
		}
		if !bytes.Equal(gotHash, wantHash) {
			// Not the plugin we've locked, so ignore it.
			continue
		}

		// If we get here then we've found a selected plugin, so we'll print
		// out its details.
		if meta.Version == "0.0.0" {
			pluginVersions = append(pluginVersions, fmt.Sprintf("+ provider.%s (unversioned)", name))
		} else {
			pluginVersions = append(pluginVersions, fmt.Sprintf("+ provider.%s v%s", name, meta.Version))
		}
	}
	if len(pluginVersions) != 0 {
		sort.Strings(pluginVersions)
		for _, str := range pluginVersions {
			c.Ui.Output(str)
		}
	}

	// If we have a version check function, then let's check for
	// the latest version as well.
	if c.CheckFunc != nil {

		// Check the latest version
		info, err := c.CheckFunc()
		if err != nil {
			c.Ui.Error(fmt.Sprintf(
				"\nError checking latest version: %s", err))
		}
		if info.Outdated {
			c.Ui.Output(fmt.Sprintf(
				"\nYour version of Terraform is out of date! The latest version\n"+
					"is %s. You can update by downloading from https://www.terraform.io/downloads.html",
				info.Latest))
		}
	}

	return 0
}

func (c *VersionCommand) Synopsis() string {
	return "Prints the Terraform version"
}
