package locals

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"errors"
	"io"
	"strings"

	e "github.com/dabory/abango-rest/etc"
)

//if keysize is 16bytes * 8bits -> 128
//if keysize is 32bytes * 8bits -> 256
//Encrypt-Decript는 plaintext가 16bytes 밖에는 지원하지 않는다 따라서
//MyAesEncrypt를 사용한다.
func MyAesEncrypt(key []byte, text []byte) ([]byte, error) {

	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, errors.New(e.FuncRunErr("odvjkwei3", e.CurrFuncName()+" "+err.Error()))
	}

	msg := Pad(text)
	ciphertext := make([]byte, aes.BlockSize+len(msg))
	iv := ciphertext[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return nil, errors.New(e.FuncRunErr("ls0ue3so", e.CurrFuncName()+" "+err.Error()))
	}

	cfb := cipher.NewCFBEncrypter(block, iv)
	cfb.XORKeyStream(ciphertext[aes.BlockSize:], msg)
	finalMsg := removeBase64Padding(base64.URLEncoding.EncodeToString(ciphertext))

	return []byte(finalMsg), nil
}

func MyAesDecrypt(key []byte, text []byte) ([]byte, error) {

	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, errors.New(e.FuncRunErr("3do8awe", e.CurrFuncName()+" "+err.Error()))
	}
	decodedMsg, err := base64.URLEncoding.DecodeString(addBase64Padding(string(text)))
	if err != nil {
		return nil, errors.New(e.FuncRunErr("mkshewjd", e.CurrFuncName()+" "+err.Error()))
	}

	if (len(decodedMsg) % aes.BlockSize) != 0 {
		return nil, errors.New(e.FuncRunErr("mskoeuwid", e.CurrFuncName()+" "+err.Error()))
	}

	iv := decodedMsg[:aes.BlockSize]
	msg := decodedMsg[aes.BlockSize:]

	cfb := cipher.NewCFBDecrypter(block, iv)
	cfb.XORKeyStream(msg, msg)

	unpadMsg, err := Unpad(msg)
	if err != nil {
		return nil, errors.New(e.FuncRunErr("012bsoo832d", e.CurrFuncName()+" "+err.Error()))
	}
	return unpadMsg, nil
}

func Pad(src []byte) []byte {
	padding := aes.BlockSize - len(src)%aes.BlockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(src, padtext...)
}

func Unpad(src []byte) ([]byte, error) {
	length := len(src)
	unpadding := int(src[length-1])

	if unpadding > length {
		return nil, errors.New(e.FuncRunErr("unpad error. This could happen when incorrect MyAesEncryption key is used", e.CurrFuncName()))
	}
	return src[:(length - unpadding)], nil
}

func addBase64Padding(value string) string {
	m := len(value) % 4
	if m != 0 {
		value += strings.Repeat("=", 4-m)
	}
	return value
}

func removeBase64Padding(value string) string {
	return strings.Replace(value, "=", "", -1)
}
