// This is a small example using readline to read a password
// and check it's strength while typing using the zxcvbn library.
// Depending on the strength the prompt is colored nicely to indicate strength.
//
// This file is licensed under the WTFPL:
//
//         DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
//                     Version 2, December 2004
//
//  Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
//
//  Everyone is permitted to copy and distribute verbatim or modified
//  copies of this license document, and changing it is allowed as long
//  as the name is changed.
//
//             DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
//    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
//
//   0. You just DO WHAT THE FUCK YOU WANT TO.
package main

import (
	"fmt"

	zxcvbn "github.com/nbutton23/zxcvbn-go"
	"github.com/wader/readline"
)

const (
	Cyan          = 36
	Green         = 32
	Magenta       = 35
	Red           = 31
	Yellow        = 33
	BackgroundRed = 41
)

// Reset sequence
var ColorResetEscape = "\033[0m"

// ColorResetEscape translates a ANSI color number to a color escape.
func ColorEscape(color int) string {
	return fmt.Sprintf("\033[0;%dm", color)
}

// Colorize the msg using ANSI color escapes
func Colorize(msg string, color int) string {
	return ColorEscape(color) + msg + ColorResetEscape
}

func createStrengthPrompt(password []rune) string {
	symbol, color := "", Red
	strength := zxcvbn.PasswordStrength(string(password), nil)

	switch {
	case strength.Score <= 1:
		symbol = "✗"
		color = Red
	case strength.Score <= 2:
		symbol = "⚡"
		color = Magenta
	case strength.Score <= 3:
		symbol = "⚠"
		color = Yellow
	case strength.Score <= 4:
		symbol = "✔"
		color = Green
	}

	prompt := Colorize(symbol, color)
	if strength.Entropy > 0 {
		entropy := fmt.Sprintf(" %3.0f", strength.Entropy)
		prompt += Colorize(entropy, Cyan)
	} else {
		prompt += Colorize(" ENT", Cyan)
	}

	prompt += Colorize(" New Password: ", color)
	return prompt
}

func main() {
	rl, err := readline.New("")
	if err != nil {
		return
	}
	defer rl.Close()

	setPasswordCfg := rl.GenPasswordConfig()
	setPasswordCfg.SetListener(func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) {
		rl.SetPrompt(createStrengthPrompt(line))
		rl.Refresh()
		return nil, 0, false
	})

	pswd, err := rl.ReadPasswordWithConfig(setPasswordCfg)
	if err != nil {
		return
	}

	fmt.Println("Your password was:", string(pswd))
}
