// Copyright (c) 2021 Andreas Auernhammer. All rights reserved.
// Use of this source code is governed by a license that can be
// found in the LICENSE file.

package minisign_test

import (
	"crypto/rand"
	"fmt"
	"io"
	"io/ioutil"
	"strconv"
	"strings"

	"aead.dev/minisign"
)

func ExampleGenerateKey() {
	// Generate a new minisign private / public key pair.
	publicKey, privateKey, err := minisign.GenerateKey(rand.Reader)
	if err != nil {
		panic(err) // TODO: error handling
	}

	// Sign a message with the private key
	message := []byte("Hello Gopher!")
	signature := minisign.Sign(privateKey, message)

	// Verify the signature with the public key and
	// print the message if the signature is valid.
	if minisign.Verify(publicKey, message, signature) {
		fmt.Println(string(message))
	}
	// Output: Hello Gopher!
}

func ExampleEncryptKey() {
	// Generate a new minisign private / public key pair.
	// We don't care about the public key in this example.
	_, privateKey, err := minisign.GenerateKey(rand.Reader)
	if err != nil {
		panic(err) // TODO: error handling
	}

	const password = "correct horse battery staple"

	// Encrypt the private key with the password
	encryptedKey, err := minisign.EncryptKey(password, privateKey)
	if err != nil {
		panic(err) // TODO: error handling
	}

	// Then, decrypt the encrypted key with the password again
	decryptedKey, err := minisign.DecryptKey(password, encryptedKey)
	if err != nil {
		panic(err) // TODO: error handling
	}

	// Now, both private keys should be identical
	fmt.Println(privateKey.Equal(decryptedKey))
	// Output: true
}

func ExampleDecryptKey() {
	const (
		rawPrivateKey = "RWRTY0IyorAWr/1gdweGki6ua7GpmoPqS+7rMBSmBy6hedA53dAAABAAAAAAAAAAAAIAAAAAwfmyB6qIIW2eGNiQaFzgs1oi52iN8cRHBPRupc9TVdfAeJvlPdvzu3TfA2DHTW2PZi98uihcr5sEB5fefFml2d0xBk72ZOGNJpOTsn95eHgEH/qUfzQZ018JfiVwWf8pNpdgNFX8ROs="
		password      = "correct horse battery staple"
	)

	// Decrypt the raw private key with the password
	privateKey, err := minisign.DecryptKey(password, []byte(rawPrivateKey))
	if err != nil {
		panic(err) // TODO: error handling
	}

	// Print the key ID as upper-case hex string
	fmt.Println("Private Key", strings.ToUpper(strconv.FormatUint(privateKey.ID(), 16)))

	// Output: Private Key A345BDA18A33D06
}

func ExampleSign() {
	const (
		rawPrivateKey = "RWRTY0IyorAWr/1gdweGki6ua7GpmoPqS+7rMBSmBy6hedA53dAAABAAAAAAAAAAAAIAAAAAwfmyB6qIIW2eGNiQaFzgs1oi52iN8cRHBPRupc9TVdfAeJvlPdvzu3TfA2DHTW2PZi98uihcr5sEB5fefFml2d0xBk72ZOGNJpOTsn95eHgEH/qUfzQZ018JfiVwWf8pNpdgNFX8ROs="
		password      = "correct horse battery staple"
	)

	// Decrypt the raw private key with the password
	privateKey, err := minisign.DecryptKey(password, []byte(rawPrivateKey))
	if err != nil {
		panic(err) // TODO: error handling
	}

	// Sign a message with the private key
	message := []byte("Hello Gopher!")
	signature := minisign.Sign(privateKey, message)

	fmt.Println(string(signature))
}

func ExampleSignWithComments() {
	const (
		rawPrivateKey = "RWRTY0IyorAWr/1gdweGki6ua7GpmoPqS+7rMBSmBy6hedA53dAAABAAAAAAAAAAAAIAAAAAwfmyB6qIIW2eGNiQaFzgs1oi52iN8cRHBPRupc9TVdfAeJvlPdvzu3TfA2DHTW2PZi98uihcr5sEB5fefFml2d0xBk72ZOGNJpOTsn95eHgEH/qUfzQZ018JfiVwWf8pNpdgNFX8ROs="
		password      = "correct horse battery staple"
	)

	// Decrypt the raw private key with the password
	privateKey, err := minisign.DecryptKey(password, []byte(rawPrivateKey))
	if err != nil {
		panic(err) // TODO: error handling
	}

	// Sign a message with comments with the private key
	const (
		trustedComment   = "This comment is signed and can be trusted"
		untrustedComment = "This comment is not signed and just informational"
	)
	message := []byte("Hello Gopher!")
	signature := minisign.SignWithComments(privateKey, message, trustedComment, untrustedComment)

	fmt.Println(string(signature))

	// Output: untrusted comment: This comment is not signed and just informational
	// RWQGPaMY2ls0CmMflCAP5J/MpaXmt+3+UoT1vRSPRjXO6w0KNtpkcQe3TxQ35kAwhjFVB6CEYYrHZmMvWjXRutefRHicRUiAJwQ=
	// trusted comment: This comment is signed and can be trusted
	// /jXXGSI/q3MhrZ5PKzL221/qC+JFVpgilf9su6AcTtMffw+9ShYt5LjU2RG1M/EspIoEv4xxK/36TeCQBgHbBw==
}

func ExampleVerify() {
	const rawPublicKey = "RWQGPaMY2ls0CkF/83ls7D+IU25w3jeYczwo3s451zDlnrJJwOdt2ro8"

	var (
		message = []byte("Hello Gopher!")

		signature = []byte(`untrusted comment: signature from private key: A345BDA18A33D06
RWQGPaMY2ls0CmMflCAP5J/MpaXmt+3+UoT1vRSPRjXO6w0KNtpkcQe3TxQ35kAwhjFVB6CEYYrHZmMvWjXRutefRHicRUiAJwQ=
trusted comment: timestamp:1600100266
2x/lxCqL+PHoT4I9Wc8PHmoNBtohgmFdWwPBON55Y2P0ttpBHgr4OFldr/Hq7nDcBGt5SBs2XjtMnxjVs6byBg==`)
	)

	var publicKey minisign.PublicKey
	if err := publicKey.UnmarshalText([]byte(rawPublicKey)); err != nil {
		panic(err) // TODO: error handling
	}

	if minisign.Verify(publicKey, message, signature) {
		fmt.Println(string(message))
	}
	// Output: Hello Gopher!
}

func ExampleReader() {
	// Generate a new minisign public / private key pair.
	publicKey, privateKey, err := minisign.GenerateKey(rand.Reader)
	if err != nil {
		panic(err) // TODO: error handling
	}

	const Message = "Hello Gopher!"

	// Sign a data stream after processing it. (Here, we just discard it)
	reader := minisign.NewReader(strings.NewReader(Message))
	if _, err := io.Copy(ioutil.Discard, reader); err != nil {
		panic(err) // TODO: error handling
	}
	signature := reader.Sign(privateKey)

	// Read a data stream and then verify its authenticity with
	// the public key.
	reader = minisign.NewReader(strings.NewReader(Message))
	message, err := ioutil.ReadAll(reader)
	if err != nil {
		panic(err) // TODO: error handling
	}
	if reader.Verify(publicKey, signature) {
		fmt.Println(string(message))
	}
	// Output: Hello Gopher!
}
