package security

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/md5"
	"crypto/rand"
	"crypto/sha512"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"strings"

	"code.revolvingcow.com/revolvingcow/loop/environment"
)

func encodeBase64(data []byte) string {
	return base64.StdEncoding.EncodeToString(data)
}

func decodeBase64(data []byte) []byte {
	b, _ := base64.StdEncoding.DecodeString(string(data))
	return b
}

func getSizedKey(key string) string {
	// Get the correct key length
	l := len(key)
	if l < 16 {
		for i := 0; i < 16-l; i++ {
			key += "."
		}
	} else if l < 24 {
		for i := 0; i < 24-l; i++ {
			key += "."
		}
	} else if l < 32 {
		for i := 0; i < 32-l; i++ {
			key += "."
		}
	} else {
		key = key[:32]
	}

	return key
}

func encrypt(text, passphrase string) ([]byte, error) {
	key := []byte(getSizedKey(passphrase))
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	cipherText := make([]byte, aes.BlockSize+len(text))
	iv := cipherText[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return nil, err
	}
	encrypter := cipher.NewCFBEncrypter(block, iv)
	encrypter.XORKeyStream(cipherText[aes.BlockSize:], []byte(text))
	return cipherText, nil
}

func decrypt(text []byte, passphrase string) ([]byte, error) {
	key := []byte(getSizedKey(passphrase))
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	if len(text) < aes.BlockSize {
		return nil, errors.New("Cipher text too short")
	}
	iv := text[:aes.BlockSize]
	data := text[aes.BlockSize:]
	decrypter := cipher.NewCFBDecrypter(block, iv)
	decrypter.XORKeyStream(data, data)
	return data, nil
}

func Hash(clearText, salt string) []byte {
	h := md5.New()
	h.Write([]byte(clearText))
	return h.Sum(nil)
}

func hashCredentials(username, passphrase, salt string) []byte {
	clearText := fmt.Sprintf(
		"%s%s-%s",
		salt,
		strings.ToLower(username),
		strings.ToLower(passphrase))

	sha := sha512.New()
	sha.Write([]byte(clearText))
	return sha.Sum(nil)
}

func GeneratePassphrase(username, passphrase string) string {
	return encodeBase64(hashCredentials(username, passphrase, environment.Salt()))
}
