package badgerDatabase

import "testing"

import "seekia/internal/helpers"
import "seekia/internal/localFilesystem"

import "sync"
import "errors"

func TestDatabase(t *testing.T) {

	err := localFilesystem.InitializeAppDatastores()
	if (err != nil){
		t.Fatal("Failed to initialize app datastores: " + err.Error())
	}

	err = startDatabase()
	if (err != nil){
		t.Fatal("Failed to start database: " + err.Error())
	}
		
	testKey, err := helpers.GetNewRandomBytes(16)
	if (err != nil){
		t.Fatal("Failed to get new random hex string: " + err.Error())
	}

	err = setDatabaseEntry(testKey, []byte("InitialValue"))
	if (err != nil) {
		t.Fatal("Failed to add test key: " + err.Error())
	}
	
	exists, retrievedValue, err := getDatabaseValue(testKey)
	if (err != nil) {
		t.Fatal("Failed to get test key value: " + err.Error())
	}
	if (exists == false){
		t.Fatal("Failed to find test key value.")
	}
	if (string(retrievedValue) != "InitialValue"){
		t.Fatal("Retrieved value does not match.")
	}

	err = setDatabaseEntry(testKey, []byte("NewValue"))
	if (err != nil){
		t.Fatal("Failed to set value: " + err.Error())
	}

	exists, keyValue, err := getDatabaseValue(testKey)
	if (err != nil) {
		t.Fatal("Failed to get test key value: " + err.Error())
	}
	if (string(keyValue) != "NewValue"){
		t.Fatal("Failed to overwrite key.")
	}

	updateValueFunction := func(entryExists bool, entryValue []byte)(bool, bool, []byte, error){

		if (entryExists == false){
			return false, false, nil, errors.New("Entry should exist during update.")
		}
		if (string(entryValue) != "NewValue"){
			return false, false, nil, errors.New("Value is unexpected during update: " + string(entryValue))
		}
		return true, true, []byte("UpdatedValue"), nil
	}

	err = updateDatabaseEntry(testKey, updateValueFunction)
	if (err != nil){
		t.Fatal("Failed to updateDatabaseEntry: " + err.Error())
	}
	
	exists, keyValue, err = getDatabaseValue(testKey)
	if (err != nil) {
		t.Fatal("Failed to get test key value: " + err.Error())
	}
	if (string(keyValue) != "UpdatedValue"){
		t.Fatal("updateDatabaseEntry failed: Unexpected value: " + string(keyValue))
	}

	err = deleteDatabaseEntry(testKey)
	if (err != nil) {
		t.Fatal("Failed to delete key: " + err.Error())
	}

	exists, _, err = getDatabaseValue(testKey)
	if (err != nil) {
		t.Fatal("Failed to get test key value: " + err.Error())
	}

	if (exists == true){
		t.Fatal("Key we deleted still exists.")
	}

	// Now we test concurrency

	var encounteredErrorMutex sync.Mutex
	var encounteredError error

	var tasksWaitgroup sync.WaitGroup

	for i:=0; i < 100; i++{

		newValue := []byte{byte(i)}

		newFunc := func(){

			err = setDatabaseEntry(testKey, newValue)
			if (err != nil){
				encounteredErrorMutex.Lock()
				encounteredError = err
				encounteredErrorMutex.Unlock()
			}

			err := deleteDatabaseEntry(testKey)
			if (err != nil){
				encounteredErrorMutex.Lock()
				encounteredError = err
				encounteredErrorMutex.Unlock()
			}

			tasksWaitgroup.Done()
		}

		tasksWaitgroup.Add(1)
		go newFunc()
	}

	tasksWaitgroup.Wait()

	if (encounteredError != nil){
		t.Fatal("Failed to set/delete database entry concurrently: " + encounteredError.Error())
	}
	

	StopDatabase()
}



