package securitymdl

import (
	"testing"

	"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/errormdl"

	"github.com/stretchr/testify/assert"

	"corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/loggermdl"
)

// ===========================================================================================
// ============================================= Config ======================================
// ===========================================================================================

func TestSetSecurityConfigBlankValues(t *testing.T) {
	secKey := ""
	initializationVector := ""
	SetSecurityConfig([]byte(secKey), initializationVector)
	assert.Equal(t, IV, "AAAAAAAAAAAAAAAA", "Matching")
}

func TestSetSecurityConfig(t *testing.T) {
	secKey := "1234567891234567"
	initializationVector := "BBBBBBBBBBBBBBBB"
	SetSecurityConfig([]byte(secKey), initializationVector)
	assert.Equal(t, IV, "BBBBBBBBBBBBBBBB", "Matching")
}

func TestSetSecurityConfigDefaultValues(t *testing.T) {
	secKey := "1234567891234567"
	initializationVector := "AAAAAAAAAAAAAAAA"
	SetSecurityConfig([]byte(secKey), initializationVector)
	assert.Equal(t, IV, "AAAAAAAAAAAAAAAA", "Matching")
}

// ===========================================================================================
// =========================================== AESEncrypt ====================================
// ===========================================================================================

func TestAESEncryptSuccess(t *testing.T) {
	plainText := "Test for success"
	key := "1234567891234567"
	encText, encError := AESEncrypt([]byte(plainText), []byte(key))
	loggermdl.LogInfo("encrypted text : ", encText)
	loggermdl.LogError("error is : ", encError)
	assert.Equal(t, "HAtVHhxx9+ULClrO1dkSKMiu6IciRmQ2PcQi4kSsLn4=", string(encText), "Encryption Successful")
}

func TestAESEncryptSmallKeyLength(t *testing.T) {
	plainText := "Test for success"
	key := "123456789123456"
	encText, encError := AESEncrypt([]byte(plainText), []byte(key))
	loggermdl.LogInfo("encrypted text : ", encText)
	loggermdl.LogError("error is : ", encError)

	assert.Error(t, encError, "Error occured due to key size")
}

func TestAESEncryptIVLessThanBlock(t *testing.T) {
	plainText := "Test for success"
	key := "1234567891234567"
	IV = "A"
	encText, encError := AESEncrypt([]byte(plainText), []byte(key))
	loggermdl.LogInfo("encrypted text : ", encText)
	loggermdl.LogError("error is : ", encError)

	assert.Error(t, encError, "IV size less than block")
}

// ===========================================================================================
// =========================================== AESDecrypt ====================================
// ===========================================================================================

func TestAESDecryptSuccess(t *testing.T) {
	cipherText := []byte("HAtVHhxx9+ULClrO1dkSKMiu6IciRmQ2PcQi4kSsLn4=")
	key := "1234567891234567"
	IV = "AAAAAAAAAAAAAAAA"
	encText, encError := AESDecrypt([]byte(cipherText), []byte(key))
	loggermdl.LogInfo("decrypted text : ", string(encText))
	loggermdl.LogError("error is : ", encError)
	assert.Equal(t, "Test for success", string(encText), "Decryption Successful")
}

func TestAESDecryptSmallKeyLength(t *testing.T) {
	cipherText := []byte("HAtVHhxx9+ULClrO1dkSKMiu6IciRmQ2PcQi4kSsLn4=")
	key := "123456789123456"
	encText, encError := AESDecrypt([]byte(cipherText), []byte(key))
	loggermdl.LogInfo("encrypted text : ", encText)
	loggermdl.LogError("error is : ", encError)

	assert.Error(t, encError, "Error occured due to key size")
}

func TestAESDecryptDecodeError(t *testing.T) {
	cipherText := []byte("HAtVHhxx9+ULClrO1dkSKMiu6IciRmQ2PcQi4kSsLn=")
	key := "123456789123456"
	encText, encError := AESDecrypt([]byte(cipherText), []byte(key))
	loggermdl.LogInfo("encrypted text : ", encText)
	loggermdl.LogError("error is : ", encError)

	assert.Error(t, encError, "Decode error")
}

func TestAESDecryptCipherLessThan1(t *testing.T) {
	cipherText := []byte("")
	key := "1234567891234567"
	encText, encError := AESDecrypt([]byte(cipherText), []byte(key))
	loggermdl.LogInfo("encrypted text : ", encText)
	loggermdl.LogError("error is : ", encError)

	assert.Error(t, encError, "CipherLessThan1")
}

func TestAESDecryptCipherSize(t *testing.T) {
	cipherText := []byte("")
	key := "1234567891234567"
	IV = "A"
	encText, encError := AESDecrypt([]byte(cipherText), []byte(key))
	loggermdl.LogInfo("encrypted text : ", encText)
	loggermdl.LogError("error is : ", encError)

	assert.Error(t, encError, "IV size less than block")
}

func TestAESDecryptIVLessThanBlock(t *testing.T) {
	cipherText := []byte("")
	key := "1234567891234567"
	IV = "A"
	encText, encError := AESDecrypt([]byte(cipherText), []byte(key))
	loggermdl.LogInfo("encrypted text : ", encText)
	loggermdl.LogError("error is : ", encError)

	assert.Error(t, encError, "IV size less than block")
}

func TestAESDecryptDifferenceCheck(t *testing.T) {
	errormdl.IsTestingNegetiveCaseOnCheckInt1 = true
	cipherText := []byte("HAtVHhxx9+ULClrO1dkSKMiu6IciRmQ2PcQi4kSsLn4=")
	key := "1234567891234567"
	IV = "AAAAAAAAAAAAAAAA"
	encText, encError := AESDecrypt([]byte(cipherText), []byte(key))
	loggermdl.LogInfo("!!!!!!!!!!!!!!!!!!!!!!!!!!encrypted text : ", encText)
	loggermdl.LogError("error is : ", encError)

	assert.Error(t, encError, " / crypto/cipher: input not full blocks")
	errormdl.IsTestingNegetiveCaseOnCheckInt1 = false
}

func TestAESDecryptDifferenceCheck2(t *testing.T) {
	errormdl.IsTestingNegetiveCaseOnCheckInt2 = true
	cipherText := []byte("HAtVHhxx9+ULClrO1dkSKMiu6IciRmQ2PcQi4kSsLn4=")
	key := "1234567891234567"
	IV = "AAAAAAAAAAAAAAAA"
	encText, encError := AESDecrypt([]byte(cipherText), []byte(key))
	loggermdl.LogInfo("!!!!!!!!!!!!!!!!!!!!!!!!!!encrypted text : ", encText)
	loggermdl.LogError("error is : ", encError)

	assert.Error(t, encError, "length of (length - unpadding) is less than 0 / crypto/cipher: input not full blocks")
	errormdl.IsTestingNegetiveCaseOnCheckInt2 = false
}

func TestCreateSecurityKey(t *testing.T) {
	errormdl.IsTestingNegetiveCaseOnCheckInt2 = true
	keyLength := 256
	key, _ := CreateSecurityKey(keyLength)
	assert.Len(t, key, keyLength, "length is equal")
	errormdl.IsTestingNegetiveCaseOnCheckInt2 = false
}
func TestCreateSecurityKeyForOutOfRange(t *testing.T) {
	errormdl.IsTestingNegetiveCaseOnCheckInt2 = true
	keyLength := 257
	_, keyerr := CreateSecurityKey(keyLength)
	loggermdl.LogError("error is : ", keyerr)
	assert.Error(t, keyerr, "length is out of range,length should be less than 256 bytes (2048 bits)")
	errormdl.IsTestingNegetiveCaseOnCheckInt2 = false
}
func BenchmarkCreateSecurityKey(b *testing.B) {
	for i := 0; i < b.N; i++ {
		CreateSecurityKey(16)
	}
}

func TestSaltPassword(t *testing.T) {
	saltedPwd, _ := SaltPassword("P@ssw0rd")
	assert.NotZero(t, len(saltedPwd), "Should give len")
}

func TestSaltPasswordError(t *testing.T) {
	errormdl.IsTestingNegetiveCaseOn = true
	_, err := SaltPassword("P@ssw0rd")
	errormdl.IsTestingNegetiveCaseOn = false
	assert.Error(t, err, "This should return error")
}
func BenchmarkSaltPassword(b *testing.B) {
	for i := 0; i < b.N; i++ {
		SaltPassword("P@ssw0rd")
	}
}

func TestCheckPasswordHash(t *testing.T) {
	match := CheckPasswordSalt("$2a$04$hers/Xb2u00e8wg4e.S7Cu7JbUm4TTR4ED3wU7HTNuuwNGJxOqMZu", "P@ssw0rd")
	assert.True(t, match)
}
func BenchmarkCheckPasswordHash(b *testing.B) {
	for i := 0; i < b.N; i++ {
		CheckPasswordSalt("$2a$04$hers/Xb2u00e8wg4e.S7Cu7JbUm4TTR4ED3wU7HTNuuwNGJxOqMZu", "P@ssw0rd")
	}
}

func TestGetHash(t *testing.T) {
	saltedPwd, _ := GetHash("P@ssw0rd")
	assert.NotZero(t, len(saltedPwd), "Should give len")
}

func TestGetHashError(t *testing.T) {
	errormdl.IsTestingNegetiveCaseOn = true
	_, err := GetHash("P@ssw0rd")
	errormdl.IsTestingNegetiveCaseOn = false
	assert.Error(t, err, "This should return error")
}

func TestGetHashAndSalt(t *testing.T) {
	saltedPwd, _ := GetHashAndSalt("P@ssw0rd", "my-salt-key")
	assert.NotZero(t, len(saltedPwd), "Should give len")
}

func TestGetHashAndSaltError(t *testing.T) {
	errormdl.IsTestingNegetiveCaseOn = true
	_, err := GetHashAndSalt("P@ssw0rd", "my-salt-key")
	errormdl.IsTestingNegetiveCaseOn = false
	assert.Error(t, err, "This should return error")
}

func TestPaddingDataUsingpkcs7(t *testing.T) {
	data := []byte("hello")
	size := 16
	paddingData, err := PaddingDataUsingpkcs7(data, size)
	if err != nil {
		assert.Errorf(t, err, "padding error : ")
	}
	expected := []byte("hello\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b")
	assert.Equal(t, expected, paddingData)
	t.Logf("Padding Data : %v", paddingData)
}

func TestUnpaddingDataUsingpkcs7(t *testing.T) {
	paddingdata := []byte{104, 101, 108, 108, 111, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11}
	size := 16
	unPaddingData, err := UnpaddingDataUsingpkcs7(paddingdata, size)
	if err != nil {
		assert.Errorf(t, err, "padding error : ")
	}
	expected := "hello"
	assert.Equal(t, expected, string(unPaddingData))
	t.Logf("Unpadding Data : %v and string is %v", unPaddingData, string(unPaddingData))
}