// Copyright 2016-2018, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
	"fmt"

	"github.com/blang/semver"
	"github.com/hashicorp/go-multierror"
	"github.com/pkg/errors"
	"github.com/spf13/cobra"

	"github.com/pulumi/pulumi/pkg/backend/display"
	"github.com/pulumi/pulumi/pkg/diag/colors"
	"github.com/pulumi/pulumi/pkg/util/cmdutil"
	"github.com/pulumi/pulumi/pkg/workspace"
)

func newPluginRmCmd() *cobra.Command {
	var all bool
	var yes bool
	var cmd = &cobra.Command{
		Use:   "rm [KIND [NAME [VERSION]]]",
		Args:  cmdutil.MaximumNArgs(3),
		Short: "Remove one or more plugins from the download cache",
		Long: "Remove one or more plugins from the download cache.\n" +
			"\n" +
			"Specify KIND, NAME, and/or VERSION to narrow down what will be removed.\n" +
			"If none are specified, the entire cache will be cleared.  If only KIND and\n" +
			"NAME are specified, but not VERSION, all versions of the plugin with the\n" +
			"given KIND and NAME will be removed.  VERSION may be a range.\n" +
			"\n" +
			"This removal cannot be undone.  If a deleted plugin is subsequently required\n" +
			"in order to execute a Pulumi program, it must be re-downloaded and installed\n" +
			"using the plugin install command.",
		Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {
			opts := display.Options{
				Color: cmdutil.GetGlobalColorization(),
			}

			// Parse the filters.
			var kind workspace.PluginKind
			var name string
			var version *semver.Range
			if len(args) > 0 {
				if !workspace.IsPluginKind(args[0]) {
					return errors.Errorf("unrecognized plugin kind: %s", kind)
				}
				kind = workspace.PluginKind(args[0])
			} else if !all {
				return errors.Errorf("please pass --all if you'd like to remove all plugins")
			}
			if len(args) > 1 {
				name = args[1]
			}
			if len(args) > 2 {
				r, err := semver.ParseRange(args[2])
				if err != nil {
					return errors.Wrap(err, "invalid plugin semver")
				}
				version = &r
			}

			// Now build a list of plugins that match.
			var deletes []workspace.PluginInfo
			plugins, err := workspace.GetPlugins()
			if err != nil {
				return errors.Wrap(err, "loading plugins")
			}
			for _, plugin := range plugins {
				if (kind == "" || plugin.Kind == kind) &&
					(name == "" || plugin.Name == name) &&
					(version == nil || (plugin.Version != nil && (*version)(*plugin.Version))) {
					deletes = append(deletes, plugin)
				}
			}

			if len(deletes) == 0 {
				return errors.New("no plugins found")
			}

			// Confirm that the user wants to do this (unless --yes was passed), and do the deletes.
			var suffix string
			if len(deletes) != 1 {
				suffix = "s"
			}
			fmt.Print(
				opts.Color.Colorize(
					fmt.Sprintf("%sThis will remove %d plugin%s from the cache:%s\n",
						colors.SpecAttention, len(deletes), suffix, colors.Reset)))
			for _, del := range deletes {
				fmt.Printf("    %s %s\n", del.Kind, del.String())
			}
			if yes || confirmPrompt("", "yes", opts) {
				var result error
				for _, plugin := range deletes {
					if err := plugin.Delete(); err != nil {
						result = multierror.Append(
							result, errors.Wrapf(err, "failed to delete %s plugin %s", plugin.Kind, plugin))
					}
				}
				if result != nil {
					return result
				}
			}

			return nil
		}),
	}

	cmd.PersistentFlags().BoolVarP(
		&all, "all", "a", false,
		"Remove all plugins")
	cmd.PersistentFlags().BoolVarP(
		&yes, "yes", "y", false,
		"Skip confirmation prompts, and proceed with removal anyway")

	return cmd
}
