package cmd

import (
	"fmt"
	"os"
	"strings"

	pkgCmd "github.com/linkerd/linkerd2/pkg/cmd"
	"github.com/linkerd/linkerd2/pkg/k8s"
	"github.com/spf13/cobra"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	yamlSep = "---\n"
)

func newCmdUninstall() *cobra.Command {
	var force bool
	var output string

	cmd := &cobra.Command{
		Use:   "uninstall",
		Args:  cobra.NoArgs,
		Short: "Output Kubernetes resources to uninstall Linkerd control plane",
		Long: `Output Kubernetes resources to uninstall Linkerd control plane.

This command provides all Kubernetes namespace-scoped and cluster-scoped resources (e.g services, deployments, RBACs, etc.) necessary to uninstall Linkerd control plane.`,
		Example: ` linkerd uninstall | kubectl delete -f -`,
		RunE: func(cmd *cobra.Command, args []string) error {

			k8sAPI, err := k8s.NewAPI(kubeconfigPath, kubeContext, impersonate, impersonateGroup, 0)
			if err != nil {
				return err
			}

			if !force {

				var fail bool
				// Retrieve any installed extensions
				extensionNamespaces, err := k8sAPI.GetAllNamespacesWithExtensionLabel(cmd.Context())
				if err != nil {
					return err
				}

				// map of the namespace and the extension name
				// Namespace is used as key so as to support custom namespace installs
				extensions := make(map[string]string)
				if len(extensionNamespaces) > 0 {
					for _, extension := range extensionNamespaces {
						extensions[extension.Name] = extension.Labels[k8s.LinkerdExtensionLabel]
					}

					// Retrieve all the extension names
					extensionNames := make([]string, 0, len(extensions))
					for _, v := range extensions {
						extensionNames = append(extensionNames, fmt.Sprintf("* %s", v))
					}

					fmt.Fprintf(os.Stderr, "Please uninstall the following extensions before uninstalling the control-plane:\n\t%s\n", strings.Join(extensionNames, "\n\t"))
					fail = true
				}

				podList, err := k8sAPI.CoreV1().Pods("").List(cmd.Context(), metav1.ListOptions{LabelSelector: k8s.ControllerNSLabel})
				if err != nil {
					return err
				}

				var injectedPods []string
				for _, pod := range podList.Items {
					// skip core control-plane namespace, and extension namespaces
					if pod.Namespace != controlPlaneNamespace && extensions[pod.Namespace] == "" {
						injectedPods = append(injectedPods, fmt.Sprintf("* %s", pod.Name))
					}
				}

				if len(injectedPods) > 0 {
					fmt.Fprintf(os.Stderr, "Please uninject the following pods before uninstalling the control-plane:\n\t%s\n", strings.Join(injectedPods, "\n\t"))
					fail = true
				}

				if fail {
					os.Exit(1)
				}
			}

			selector, err := pkgCmd.GetLabelSelector(k8s.ControllerNSLabel)
			if err != nil {
				return err
			}

			err = pkgCmd.Uninstall(cmd.Context(), k8sAPI, selector, output)
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
			}

			return nil
		},
	}

	cmd.Flags().BoolVarP(&force, "force", "f", force, "Force uninstall even if there exist non-control-plane injected pods")
	cmd.PersistentFlags().StringVarP(&output, "output", "o", "yaml", "Output format. One of: json|yaml")
	return cmd
}
