package configuration

import (
	"fmt"

	"code.justin.tv/d8a/buddy/cmd/buddy-cli/data"
	"code.justin.tv/d8a/buddy/lib/clusters/clusterdb"
	"code.justin.tv/d8a/buddy/lib/config"
	"code.justin.tv/d8a/buddy/lib/sandstorm"
	"code.justin.tv/d8a/buddy/lib/store"
	"code.justin.tv/d8a/buddy/lib/terminal"
	"code.justin.tv/systems/sandstorm/manager"
	"github.com/fatih/color"
	"github.com/pkg/errors"
	"github.com/spf13/cobra"
)

var userUpsert = &cobra.Command{
	Use:   "user [cluster] <optional: [secret]>",
	Short: "Add a new user to a cluster or modify the user's sandstorm secret",
	Long:  "Add a new user to a cluster or modify the user's sandstorm secret",
	RunE: func(command *cobra.Command, args []string) error {
		if len(data.ConfigureData.Args()) < 1 || len(data.ConfigureData.Args()) > 2 {
			return command.Usage()
		}

		foundCluster := data.ConfigureData.FoundCluster()

		username := data.ConfigureData.Args()[0]
		secret := data.ConfigureData.SandstormClient().DefaultSecretPath(foundCluster.Name, username)
		if len(data.ConfigureData.Args()) > 1 {
			secret = data.ConfigureData.Args()[1]
		}

		clusterDb, err := clusterdb.OpenDbConn(foundCluster, data.ConfigureData.SandstormClient(), foundCluster.SuperUser)
		if err != nil {
			return errors.Wrap(err, fmt.Sprintf("could not open superuser connection to cluster %s", foundCluster.Name))
		}

		userMap, err := clusterDb.ClusterUsers()
		if err != nil {
			return errors.Wrap(err, fmt.Sprintf("could not retrieve user list for cluster %s", foundCluster.Name))
		}

		canlogin, ok := userMap[username]
		if !ok {
			return fmt.Errorf("there is no user %s in cluster %s- create it in the database and set up access grants before adding it to buddy", username, foundCluster.Name)
		}

		if !canlogin && clusterDb.CanLockUsers() {
			swapLogin, err := terminal.AskForConfirmation(fmt.Sprintf("User %s is set to NOLOGIN- would you like to change that?", username))
			if err != nil {
				return err
			}
			if swapLogin {
				err := clusterDb.SetUserLogin(username, true)
				if err != nil {
					return errors.Wrap(err, fmt.Sprintf("could not set user %s on cluster %s to LOGIN", username, foundCluster.Name))
				}
			}
		}

		secretObj, err := data.ConfigureData.SandstormClient().Manager().Get(secret)
		if err != nil {
			return errors.Wrap(err, fmt.Sprintf("issue retrieving secret '%s'- secret may not exist", secret))
		}

		if secretObj == nil {
			shouldAddSecret, err := terminal.AskForConfirmation(fmt.Sprintf("Secret %s does not exist in sandstorm, but you (probably) have write access- generate a password and add the secret?", secret))
			if err != nil {
				return err
			}
			if !shouldAddSecret {
				return fmt.Errorf("add the secret and try again")
			}
			err = data.ConfigureData.SandstormClient().Manager().Put(&manager.Secret{
				Name:      secret,
				Plaintext: []byte(sandstorm.GeneratePassword()),
			})
			if err != nil {
				return errors.Wrap(err, fmt.Sprintf("could not write '%s' to sandstorm", secret))
			}
		}

		foundUser := foundCluster.GetUser(username)

		if foundUser != nil {
			foundUser.Secret = secret
			err = store.UpdateUser(data.ConfigureData.StoreDB(), foundUser)
		} else {
			err = store.InsertUser(data.ConfigureData.StoreDB(), foundCluster, &config.User{
				Name:   username,
				Secret: secret,
			})
		}

		if err != nil {
			return errors.Wrap(err, fmt.Sprintf("couldn't write user %s to buddy store", username))
		}

		err = sandstorm.UpdateTemplate(foundCluster, username, secret)
		if err != nil {
			return errors.Wrap(err, fmt.Sprintf("could not write sandstorm templates for user %s to disk", username))
		}
		_, err = sandstorm.WritePasswordFiles(data.ConfigureData.ConfigFile().Cluster)
		if err != nil {
			color.Red("%v", err)
		}
		err = sandstorm.Reload()
		if err != nil {
			return errors.Wrap(err, "could not issue SIGHUP to sandstorm agent")
		}
		return nil
	},
}

func init() {
	ConfigureCmd.AddCommand(userUpsert)
}
