/*
Copyright (c) Edgeless Systems GmbH

SPDX-License-Identifier: AGPL-3.0-only
*/

package cmd

import (
	"errors"
	"fmt"
	"io/fs"

	"github.com/spf13/afero"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
	"github.com/edgelesssys/constellation/v2/internal/constants"
	"github.com/edgelesssys/constellation/v2/internal/file"
)

// NewTerminateCmd returns a new cobra.Command for the terminate command.
func NewTerminateCmd() *cobra.Command {
	cmd := &cobra.Command{
		Use:   "terminate",
		Short: "Terminate a Constellation cluster",
		Long: "Terminate a Constellation cluster.\n\n" +
			"The cluster can't be started again, and all persistent storage will be lost.",
		Args: cobra.NoArgs,
		RunE: runTerminate,
	}
	cmd.Flags().BoolP("yes", "y", false, "terminate the cluster without further confirmation")
	return cmd
}

type terminateFlags struct {
	rootFlags
	yes bool
}

func (f *terminateFlags) parse(flags *pflag.FlagSet) error {
	if err := f.rootFlags.parse(flags); err != nil {
		return err
	}

	yes, err := flags.GetBool("yes")
	if err != nil {
		return fmt.Errorf("getting 'yes' flag: %w", err)
	}
	f.yes = yes
	return nil
}

// runTerminate runs the terminate command.
func runTerminate(cmd *cobra.Command, _ []string) error {
	spinner, err := newSpinnerOrStderr(cmd)
	if err != nil {
		return fmt.Errorf("creating spinner: %w", err)
	}
	defer spinner.Stop()
	terminator := cloudcmd.NewTerminator()

	logger, err := newCLILogger(cmd)
	if err != nil {
		return fmt.Errorf("creating logger: %w", err)
	}

	t := &terminateCmd{log: logger, fileHandler: file.NewHandler(afero.NewOsFs())}
	if err := t.flags.parse(cmd.Flags()); err != nil {
		return err
	}

	return t.terminate(cmd, terminator, spinner)
}

type terminateCmd struct {
	log         debugLog
	fileHandler file.Handler
	flags       terminateFlags
}

func (t *terminateCmd) terminate(cmd *cobra.Command, terminator cloudTerminator, spinner spinnerInterf) error {
	if !t.flags.yes {
		cmd.Println("You are about to terminate a Constellation cluster.")
		cmd.Println("All of its associated resources will be DESTROYED.")
		cmd.Println("This action is irreversible and ALL DATA WILL BE LOST.")
		ok, err := askToConfirm(cmd, "Do you want to continue?")
		if err != nil {
			return err
		}
		if !ok {
			cmd.Println("The termination of the cluster was aborted.")
			return nil
		}
	}

	spinner.Start("Terminating", false)
	err := terminator.Terminate(cmd.Context(), constants.TerraformWorkingDir, t.flags.tfLogLevel)
	spinner.Stop()
	if err != nil {
		return fmt.Errorf("terminating Constellation cluster: %w", err)
	}

	cmd.Println("Your Constellation cluster was terminated successfully.")

	var removeErr error
	if err := t.fileHandler.Remove(constants.AdminConfFilename); err != nil && !errors.Is(err, fs.ErrNotExist) {
		removeErr = errors.Join(err, fmt.Errorf("failed to remove file: '%s', please remove it manually", t.flags.pathPrefixer.PrefixPrintablePath(constants.AdminConfFilename)))
	}

	if err := t.fileHandler.Remove(constants.StateFilename); err != nil && !errors.Is(err, fs.ErrNotExist) {
		removeErr = errors.Join(err, fmt.Errorf("failed to remove file: '%s', please remove it manually", t.flags.pathPrefixer.PrefixPrintablePath(constants.StateFilename)))
	}

	return removeErr
}