package main

import (
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/spf13/cobra"
	"golang.org/x/crypto/bcrypt"
)

var (
	hashPassword  string
	hashCost      int
	hashFromStdin bool
)

var passwordHashCmd = &cobra.Command{
	Use:   "password-hash [password]",
	Short: "Generate a bcrypt password hash",
	Long:  "Generate a bcrypt password hash for creating or updating users.",
	Args:  cobra.MaximumNArgs(1),
	Annotations: map[string]string{
		"skip-config-load": "true",
	},
	Run: func(cmd *cobra.Command, args []string) {
		checkError(runPasswordHash(args))
	},
}

func init() {
	passwordHashCmd.Flags().StringVarP(&hashPassword, "password", "p", "", "Password to hash")
	passwordHashCmd.Flags().IntVar(&hashCost, "cost", bcrypt.DefaultCost, "Bcrypt cost")
	passwordHashCmd.Flags().BoolVar(&hashFromStdin, "stdin", false, "Read password from stdin")
}

func runPasswordHash(args []string) error {
	if hashCost < bcrypt.MinCost || hashCost > bcrypt.MaxCost {
		return fmt.Errorf("invalid cost %d (must be between %d and %d)", hashCost, bcrypt.MinCost, bcrypt.MaxCost)
	}

	password, err := resolveHashPassword(args)
	if err != nil {
		return err
	}

	hashed, err := bcrypt.GenerateFromPassword([]byte(password), hashCost)
	if err != nil {
		return fmt.Errorf("failed to hash password: %w", err)
	}

	fmt.Println(string(hashed))
	return nil
}

func resolveHashPassword(args []string) (string, error) {
	if hashFromStdin && (hashPassword != "" || len(args) > 0) {
		return "", fmt.Errorf("use --stdin by itself (do not combine with --password or positional password)")
	}

	if hashFromStdin {
		data, err := io.ReadAll(os.Stdin)
		if err != nil {
			return "", fmt.Errorf("failed to read password from stdin: %w", err)
		}
		password := strings.TrimRight(string(data), "\r\n")
		if password == "" {
			return "", fmt.Errorf("password cannot be empty")
		}
		return password, nil
	}

	if hashPassword != "" && len(args) > 0 {
		return "", fmt.Errorf("use either --password or positional password, not both")
	}

	if hashPassword != "" {
		return hashPassword, nil
	}

	if len(args) > 0 {
		return args[0], nil
	}

	return readPassword("Password: "), nil
}
