package password

import (
	"math/rand"

	"github.com/pingcap/errors"
)

const (
	// ExtraCharsCount is the number of extra characters that will be added to the password
	ExtraCharsCount = 4
)

// specialChars represents few special characters that can be used in password.
var specialChars = []rune{'!', '@', '#', '$', '%', '^', '&', '*', '_', '+'}

// Decorate converts the text so that it contains at least one upper case letter,
// one lower case letter, one number and one special character
func Decorate(text string) string {
	// Calculate the ASCII sum of the text
	asciiSum := 0
	for _, c := range text {
		asciiSum += int(c)
	}
	upper := rune(asciiSum%26 + int('A'))
	lower := rune(asciiSum%26 + int('a'))
	number := rune(asciiSum%10 + int('0'))
	special := specialChars[asciiSum%len(specialChars)]

	text += string([]rune{upper, lower, number, special})
	asciiSum += int(upper) + int(lower) + int(number) + int(special)
	shuffledText := shuffle(text, int64(asciiSum))
	return shuffledText
}

func shuffle(originalText string, seed int64) string {
	r := rand.New(rand.NewSource(seed))
	runeText := []rune(originalText)
	shuffledText := make([]rune, len(runeText))
	perm := r.Perm(len(runeText))
	for i, j := range perm {
		shuffledText[i] = runeText[j]
	}
	return string(shuffledText)
}

func reverseShuffle(shuffledText string, seed int64) string {
	r := rand.New(rand.NewSource(seed))
	perm := r.Perm(len(shuffledText))
	original := make([]rune, len(shuffledText))
	for i, j := range perm {
		original[j] = rune(shuffledText[i])
	}
	return string(original)
}

// ReverseDecorate reverses the decoration and returns the original text.
func ReverseDecorate(decorated string) (string, error) {
	asciiSum := 0
	for _, c := range decorated {
		asciiSum += int(c)
	}
	original := reverseShuffle(decorated, int64(asciiSum))
	if len(original) < ExtraCharsCount {
		return "", errors.New("invalid decorated text")
	}
	original = original[:len(original)-ExtraCharsCount]
	return original, nil
}
