package commands

import (
	"fmt"

	"github.com/manifoldco/promptui"
	"github.com/urfave/cli/v2"

	"github.com/photoprism/photoprism/internal/config"
	"github.com/photoprism/photoprism/internal/entity"
	"github.com/photoprism/photoprism/internal/form"
	"github.com/photoprism/photoprism/pkg/authn"
	"github.com/photoprism/photoprism/pkg/clean"
	"github.com/photoprism/photoprism/pkg/txt"
)

// UsersAddCommand configures the command name, flags, and action.
var UsersAddCommand = &cli.Command{
	Name:      "add",
	Usage:     "Creates a new user account",
	ArgsUsage: "[username]",
	Flags:     UserFlags,
	Action:    usersAddAction,
}

// usersAddAction adds a new user account.
func usersAddAction(ctx *cli.Context) error {
	return CallWithDependencies(ctx, func(conf *config.Config) error {
		conf.MigrateDb(false, nil)

		frm := form.NewUserFromCli(ctx)

		interactive := true

		if frm.UserName != "" && frm.Password != "" {
			log.Debugf("user will be added in non-interactive mode")
			interactive = false
		}

		if interactive && frm.UserName == "" {
			prompt := promptui.Prompt{
				Label: "Username",
			}

			res, err := prompt.Run()

			if err != nil {
				return err
			}

			frm.UserName = clean.Username(res)
		}

		// Check if account exists but is deleted.
		if frm.UserName == "" {
			return authn.ErrUsernameRequired
		} else if m := entity.FindUserByName(frm.UserName); m != nil {
			if !m.IsDeleted() {
				return authn.ErrAccountAlreadyExists
			}

			prompt := promptui.Prompt{
				Label:     fmt.Sprintf("Restore user %s?", m.String()),
				IsConfirm: true,
			}

			if _, err := prompt.Run(); err != nil {
				return authn.ErrAccountAlreadyExists
			}

			if err := m.RestoreFromCli(ctx, frm.Password); err != nil {
				return err
			}

			log.Infof("user %s has been restored", m.String())

			return nil
		}

		// Enter account email.
		if interactive && frm.UserEmail == "" && frm.Provider().SupportsPasswordAuthentication() {
			prompt := promptui.Prompt{
				Label: "Email",
			}

			res, err := prompt.Run()

			if err != nil {
				return err
			}

			frm.UserEmail = clean.Email(res)
		}

		// Enter account password.
		if interactive && (frm.Provider().RequiresLocalPassword() || frm.Password != "") &&
			len([]rune(ctx.String("password"))) < entity.PasswordLength {
			validate := func(input string) error {
				if len([]rune(input)) < entity.PasswordLength {
					return fmt.Errorf("password must have at least %d characters", entity.PasswordLength)
				} else if len(input) > txt.ClipPassword {
					return authn.ErrPasswordTooLong
				}
				return nil
			}
			prompt := promptui.Prompt{
				Label:    "Password",
				Validate: validate,
				Mask:     '*',
			}
			resPasswd, err := prompt.Run()
			if err != nil {
				return err
			}
			validateRetype := func(input string) error {
				if input != resPasswd {
					return authn.ErrPasswordsDoNotMatch
				}
				return nil
			}
			confirm := promptui.Prompt{
				Label:    "Retype Password",
				Validate: validateRetype,
				Mask:     '*',
			}
			resConfirm, err := confirm.Run()
			if err != nil {
				return err
			}
			if resConfirm != resPasswd {
				return authn.ErrInvalidPassword
			} else {
				frm.Password = resPasswd
			}
		}

		return entity.AddUser(frm)
	})
}
