/*
Copyright The Helm Authors.

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"
	"io"
	"strconv"
	"time"

	"github.com/spf13/cobra"

	"helm.sh/helm/v4/pkg/action"
	"helm.sh/helm/v4/pkg/cmd/require"
)

const rollbackDesc = `
This command rolls back a release to a previous revision.

The first argument of the rollback command is the name of a release, and the
second is a revision (version) number. If this argument is omitted or set to
0, it will roll back to the previous release.

To see revision numbers, run 'helm history RELEASE'.
`

func newRollbackCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
	client := action.NewRollback(cfg)

	cmd := &cobra.Command{
		Use:   "rollback <RELEASE> [REVISION]",
		Short: "roll back a release to a previous revision",
		Long:  rollbackDesc,
		Args:  require.MinimumNArgs(1),
		ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
			if len(args) == 0 {
				return compListReleases(toComplete, args, cfg)
			}

			if len(args) == 1 {
				return compListRevisions(toComplete, cfg, args[0])
			}

			return noMoreArgsComp()
		},
		RunE: func(cmd *cobra.Command, args []string) error {
			if len(args) > 1 {
				ver, err := strconv.Atoi(args[1])
				if err != nil {
					return fmt.Errorf("could not convert revision to a number: %w", err)
				}
				client.Version = ver
			}

			dryRunStrategy, err := cmdGetDryRunFlagStrategy(cmd, false)
			if err != nil {
				return err
			}
			client.DryRunStrategy = dryRunStrategy

			if err := client.Run(args[0]); err != nil {
				return err
			}

			fmt.Fprint(out, "Rollback was a success! Happy Helming!\n")
			return nil
		},
	}

	f := cmd.Flags()
	f.BoolVar(&client.ForceReplace, "force-replace", false, "force resource updates by replacement")
	f.BoolVar(&client.ForceReplace, "force", false, "deprecated")
	f.MarkDeprecated("force", "use --force-replace instead")
	f.BoolVar(&client.ForceConflicts, "force-conflicts", false, "if set server-side apply will force changes against conflicts")
	f.StringVar(&client.ServerSideApply, "server-side", "auto", "must be \"true\", \"false\" or \"auto\". Object updates run in the server instead of the client (\"auto\" defaults the value from the previous chart release's method)")
	f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during rollback")
	f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
	f.BoolVar(&client.WaitForJobs, "wait-for-jobs", false, "if set and --wait enabled, will wait until all Jobs have been completed before marking the release as successful. It will wait for as long as --timeout")
	f.BoolVar(&client.CleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this rollback when rollback fails")
	f.IntVar(&client.MaxHistory, "history-max", settings.MaxHistory, "limit the maximum number of revisions saved per release. Use 0 for no limit")
	addDryRunFlag(cmd)
	AddWaitFlag(cmd, &client.WaitStrategy)
	cmd.MarkFlagsMutuallyExclusive("force-replace", "force-conflicts")
	cmd.MarkFlagsMutuallyExclusive("force", "force-conflicts")

	return cmd
}
