// Copyright (c) 2020, Andreas Schneider
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the <organization> nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package main

import (
	"bytes"
	"fmt"
	"math/rand"

	pwgen "github.com/sethvargo/go-password/password"
	"golang.org/x/crypto/bcrypt"
	"golang.org/x/crypto/ssh/terminal"
)

type PasswordParam struct {
	Password               string `name:"password" help:"The password to be set. Empty to prompt."`
	GeneratePassword       bool   `name:"generate-password" help:"If set, the password is auto generated."`
	GeneratePasswordLength int    `name:"password-length" help:"If generate-password is set, this specified the length of the generated password." default:"32"`
}

func (p *PasswordParam) acquirePassword() (string, error) {
	if p.GeneratePassword {
		// math.rand is not secure. For determining the number of digits in a password
		// it should suffice, though. Beware that we expect here that we at least initialized
		// the seed somewhere.
		pw, err := pwgen.Generate(p.GeneratePasswordLength, rand.Intn(p.GeneratePasswordLength/2), 0, false, true)
		if err != nil {
			return "", fmt.Errorf("cannot generate password: %w", err)
		}

		p.Password = pw

		fmt.Printf("Password generated: %s\n", p.Password)
	}

	if p.Password == "" {
		fmt.Printf("Enter password: ")
		bytePassword, err := terminal.ReadPassword(0)
		if err != nil {
			fmt.Println()
			return "", fmt.Errorf("error reading password: %w", err)
		}
		fmt.Printf("\nRepeat password: ")
		bytePasswordRepeat, err := terminal.ReadPassword(0)
		if err != nil {
			fmt.Println()
			return "", fmt.Errorf("error reading password: %w", err)
		}
		fmt.Println()

		if !bytes.Equal(bytePassword, bytePasswordRepeat) {
			return "", fmt.Errorf("passwords do not match")
		}

		p.Password = string(bytePassword)
	}

	if p.Password == "" {
		return "", fmt.Errorf("empty password supplied")
	}

	hash, err := bcrypt.GenerateFromPassword([]byte(p.Password), 0)
	if err != nil {
		return "", fmt.Errorf("cannot hash password: %w", err)
	}

	return string(hash), nil
}
