package encryptor

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"io"
	"io/ioutil"

	"golang.org/x/crypto/openpgp"
	_ "golang.org/x/crypto/ripemd160"
)

// Encryptor interface
type Encryptor interface {
	Encrypt(string) string
	Decrypt() string
}

type encryptor struct {
	entity *openpgp.Entity
}

// NewEncryptor function
func NewEncryptor(private, public string) *encryptor {
	entity, err := openpgp.NewEntity("", "", "", nil)
	if err != nil {
		panic(err)
	}
	entity.PrimaryKey.PubKeyAlgo
	entity.PrimaryKey.PublicKey
	entity.PrivateKey.PubKeyAlgo
	entity.PrivateKey.PrivateKey
	fmt.Println(fmt.Sprintf("%T", entity.PrimaryKey.PublicKey))
	bufpri := new(bytes.Buffer)
	entity.SerializePrivate(bufpri, nil)
	bufpub := new(bytes.Buffer)
	entity.Serialize(bufpub)
	bufPr, _ := b64enc(bufpri)
	bufPu, _ := b64enc(bufpub)
	fmt.Printf("PRIVATE: %v\nPUBLIC: %v\n", bufPr[10:20], bufPu[10:20])
	return &encryptor{
		entity: entity,
	}
}

func b64enc(msg io.Reader) (string, error) {
	bytes, err := ioutil.ReadAll(msg)
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(bytes), nil
}

func b64dec(msg string) (io.Reader, error) {
	decMe, err := base64.StdEncoding.DecodeString(msg)
	if err != nil {
		return nil, err
	}
	return bytes.NewBuffer(decMe), nil
}

// Encrypt function
func (e *encryptor) Encrypt(msg string) (string, error) {
	// encrypt string
	buf := new(bytes.Buffer)
	w, err := openpgp.Encrypt(buf, []*openpgp.Entity{e.entity}, nil, nil, nil)
	if err != nil {
		return "", err
	}
	_, err = w.Write([]byte(msg))
	if err != nil {
		return "", err
	}
	err = w.Close()
	if err != nil {
		return "", err
	}
	// Encode to base64
	return b64enc(buf)
}

// Decrypt function
func (e *encryptor) Decrypt(msg string) (string, error) {
	// Decode the base64 string
	decMe, err := b64dec(msg)
	if err != nil {
		panic(err)
	}

	md, err := openpgp.ReadMessage(decMe, openpgp.EntityList([]*openpgp.Entity{e.entity}), nil, nil)
	if err != nil {
		return "", err
	}

	plaintext, err := ioutil.ReadAll(md.UnverifiedBody)
	if err != nil {
		return "", err
	}

	return string(plaintext), nil
}
