package myMapList_test

import "testing"

import "seekia/internal/myDatastores/myMapList"

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

import "sync"

func TestAddDeleteMapListData(t *testing.T) {
	
	err := localFilesystem.InitializeAppDatastores()
	if (err != nil){
		t.Fatal("Failed to initialize app datastores: " + err.Error())
	}
	err = appUsers.InitializeAppUserForTests(false, false)
	if (err != nil){
		t.Fatal("Failed to initalize app user for tests: " + err.Error())
	}

	newMapListObject, err := myMapList.CreateNewMapList("TestMapList")
	if (err != nil) {
		t.Fatal("Failed to create new map: " + err.Error())
	}

	err = newMapListObject.DeleteMapList()
	if (err != nil) {
		t.Fatal("Failed to delete map list: " + err.Error())
	}

	_, err = newMapListObject.GetMapList()
	if (err != nil) {
		t.Fatal("Failed to get new map list: " + err.Error())
	}

	newMapA := map[string]string{
		"TestKeyA": "TestValueA",
	}
	err = newMapListObject.AddMapListItem(newMapA)
	if (err != nil) {
		t.Fatal("Failed to add map list item: " + err.Error())
	}

	newMapB := map[string]string{
		"TestKeyB": "TestValueB",
	}
	err = newMapListObject.AddMapListItem(newMapB)
	if (err != nil) {
		t.Fatal("Failed to add map list item: " + err.Error())
	}

	lookupMapA := map[string]string{
		"TestKeyA": "WrongValue",
	}
	exists, retrievedMapListItems, err := newMapListObject.GetMapListItems(lookupMapA)
	if (err != nil) {
		t.Fatal("Failed to get map list items: " + err.Error())
	}
	if (exists == true) {
		t.Fatal("Failed to get map list item: Returning items we should not get.")
	}

	lookupMapB := map[string]string{
		"TestKeyA": "TestValueA",
	}
	exists, retrievedMapListItems, err = newMapListObject.GetMapListItems(lookupMapB)
	if (err != nil) {
		t.Fatal("Failed to get map list items: " + err.Error())
	}
	if (exists == false) {
		t.Fatal("Failed to get map list item.")
	}

	if (len(retrievedMapListItems) != 1){
		t.Fatal("Map list retrieved items list is not correct length.")
	}

	returnedMap := retrievedMapListItems[0]

	if (len(returnedMap) != 1){
		t.Fatal("Map list value does not match.")
	}

	returnedValue, exists := returnedMap["TestKeyA"]
	if (exists == false) {
		t.Fatal("Map list retrieved item TestKeyA key not found.")	
	}
	if (returnedValue != "TestValueA"){ 
		 t.Fatal("Map list retrieved item value does not match.")
	}

	err = newMapListObject.DeleteMapListItems(lookupMapB)
	if (err != nil){
		t.Fatal("Failed to delete map list items: " + err.Error())
	}

	exists, _, err = newMapListObject.GetMapListItems(lookupMapB)
	if (err != nil) {
		t.Fatal("Failed to get map list items: " + err.Error())
	}
	if (exists == true) {
		t.Fatal("Failed to delete map list item.")
	}

	err = newMapListObject.DeleteMapList()
	if (err != nil) {
		t.Fatal("Failed to delete map list: " + err.Error())
	}	
	
	exists, _, err = newMapListObject.GetMapListItems(newMapB)
	if (err != nil) {
		t.Fatal("Failed to get map list items: " + err.Error())
	}
	if (exists == true) {
		t.Fatal("Failed to delete map list.")
	}

	// Now we test concurrency

	newMapC := map[string]string{
		"TestKeyC": "TestValueC",
	}
	err = newMapListObject.AddMapListItem(newMapC)
	if (err != nil) {
		t.Fatal("Failed to add map list item: " + err.Error())
	}

	currentMapList, err := newMapListObject.GetMapList()
	if (err != nil) {
		t.Fatal("Failed to get map: " + err.Error())
	}

	// Now we test concurrency

	var mapWaitgroup sync.WaitGroup

	var errorMutex sync.Mutex
	var errorEncountered error

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

		mapWaitgroup.Add(1)

		indexString := helpers.ConvertIntToString(i)

		addFunction := func(){

			newItem := make(map[string]string)
			newItem["TestKey" + indexString] = "TestValue" + indexString

			err := newMapListObject.AddMapListItem(newItem)
			if (err != nil) {
				errorMutex.Lock()
				errorEncountered = err
				errorMutex.Unlock()
			}

			mapWaitgroup.Done()	
		}
		
		go addFunction()
	}

	mapWaitgroup.Wait()

	if (errorEncountered != nil){
		t.Fatal(errorEncountered.Error())
	}

	if (len(currentMapList) != 1){
		t.Fatal("Map list not copied.")
	}

	err = newMapListObject.DeleteMapList()
	if (err != nil) {
		t.Fatal("Failed to delete map list: " + err.Error())
	}	
}


func TestMapListCopying(t *testing.T){

	err := localFilesystem.InitializeAppDatastores()
	if (err != nil){
		t.Fatal("Failed to initialize app datastores: " + err.Error())
	}
	err = appUsers.InitializeAppUserForTests(false, false)
	if (err != nil){
		t.Fatal("Failed to initalize app user for tests: " + err.Error())
	}

	newMapListObject, err := myMapList.CreateNewMapList("TestMapList")
	if (err != nil) {
		t.Fatal("Failed to create new map: " + err.Error())
	}

	err = newMapListObject.DeleteMapList()
	if (err != nil) {
		t.Fatal("Failed to delete map list: " + err.Error())
	}

	testMapList := make([]map[string]string, 0, 1)

	testMapA := map[string]string{
		"TestMapA": "TestValueA",
	}

	testMapList = append(testMapList, testMapA)
	
	err = newMapListObject.OverwriteMapList(testMapList)
	if (err != nil) {
		t.Fatal("Failed to overwrite map list: " + err.Error())
	}

	existingMapListA, err := newMapListObject.GetMapList()
	if (err != nil) {
		t.Fatal("Failed to get map list: " + err.Error())
	}

	existingMapA := existingMapListA[0]
	existingMapA["TestMapC"] = "TestValueC"

	existingMapB, err := newMapListObject.GetMapList()
	if (err != nil) {
		t.Fatal("Failed to get map list: " + err.Error())
	}

	existingMapItem := existingMapB[0]

	_, exists := existingMapItem["TestMapC"]
	if (exists == true) {
		t.Fatal("Map list not copied on GetMapList.")
	}
}



