package auth

import (
	"errors"
	"unicode"

	"golang.org/x/crypto/bcrypt"
)

const (
	// BcryptCost is the bcrypt hashing cost. 12 balances security and performance.
	BcryptCost = 12

	// MinPasswordLength is the minimum allowed password length.
	MinPasswordLength = 8
	// MaxPasswordLength is the maximum allowed password length (bcrypt limit).
	MaxPasswordLength = 72
)

var (
	// ErrPasswordTooShort is returned when a password is below the minimum length.
	ErrPasswordTooShort = errors.New("password must be at least 8 characters")
	// ErrPasswordTooLong is returned when a password exceeds the bcrypt limit.
	ErrPasswordTooLong = errors.New("password must be at most 72 characters")
	// ErrPasswordWeak is returned when a password lacks required character types.
	ErrPasswordWeak = errors.New("password must contain at least one uppercase letter, one lowercase letter, and one number")
)

// HashPassword hashes a plaintext password using bcrypt.
func HashPassword(password string) (string, error) {
	hash, err := bcrypt.GenerateFromPassword([]byte(password), BcryptCost)
	if err != nil {
		return "", err
	}
	return string(hash), nil
}

// CheckPassword compares a plaintext password against a bcrypt hash.
// Returns true if the password matches.
func CheckPassword(password, hash string) bool {
	return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) == nil
}

// ValidatePasswordStrength checks that a password meets minimum complexity requirements.
// Returns nil if the password is acceptable.
func ValidatePasswordStrength(password string) error {
	if len(password) < MinPasswordLength {
		return ErrPasswordTooShort
	}
	if len(password) > MaxPasswordLength {
		return ErrPasswordTooLong
	}

	var hasUpper, hasLower, hasDigit bool
	for _, r := range password {
		switch {
		case unicode.IsUpper(r):
			hasUpper = true
		case unicode.IsLower(r):
			hasLower = true
		case unicode.IsDigit(r):
			hasDigit = true
		}
	}

	if !hasUpper || !hasLower || !hasDigit {
		return ErrPasswordWeak
	}

	return nil
}
