package profileFormat_test

import "seekia/internal/profiles/profileFormat"
import "seekia/internal/helpers"
import "seekia/internal/genetics/geneticReferences/monogenicDiseases"
import "seekia/internal/genetics/geneticReferences/polygenicDiseases"
import "seekia/internal/genetics/geneticReferences/traits"

import "testing"
import "strings"


func TestProfileFormat(t *testing.T){

	err := polygenicDiseases.InitializePolygenicDiseaseVariables()
	if (err != nil) {
		t.Fatal("InitializePolygenicDiseaseVariables failed: " + err.Error())
	}

	err = traits.InitializeTraitVariables()
	if (err != nil) {
		t.Fatal("InitializeTraitVariables failed: " + err.Error())
	}

	err = profileFormat.InitializeProfileFormatVariables()
	if (err != nil){
		t.Fatal("Failed to initialize profile format variables: " + err.Error())
	}

	// We use this map to detect duplicate attribute identifiers
	attributeIdentifiersMap := make(map[int]struct{})

	// We use this map to detect duplicate attribute names
	attributeNamesMap := make(map[string]struct{})

	// We store all mandatory attributes in this map
	allMandatoryAttributesMap := make(map[string]struct{})

	profileAttributeObjectsList, err := profileFormat.GetProfileAttributeObjectsList()
	if (err != nil){
		t.Fatal("GetProfileAttributeObjectsList failed: " + err.Error())
	}

	for _, attributeObject := range profileAttributeObjectsList{

		profileVersionsList := attributeObject.ProfileVersions
		attributeIdentifier := attributeObject.AttributeIdentifier
		attributeName := attributeObject.AttributeName
		getIsRequiredFunction := attributeObject.GetIsRequired
		getMandatoryAttributesFunction := attributeObject.GetMandatoryAttributes
		getProfileTypesFunction := attributeObject.GetProfileTypes
		getIsCanonicalFunction := attributeObject.GetIsCanonical
		checkValueFunction := attributeObject.CheckValueFunction

		if (profileVersionsList == nil){
			t.Fatal(attributeName + " Attribute object missing profile versions list.")
		}
		if (attributeIdentifier == 0){
			t.Fatal(attributeName + " Attribute object missing attribute identifier.")
		}
		if (attributeIdentifier < 1 || attributeIdentifier > 4294967295){
			t.Fatal(attributeName + " Attribute object contains attribute identifier out of range.")
		}
		if (attributeName == ""){
			t.Fatal(attributeName + " Attribute object missing attribute name.")
		}
		if (getIsRequiredFunction == nil){
			t.Fatal(attributeName + " Attribute object missing getIsRequiredFunction")
		}
		if (getMandatoryAttributesFunction == nil){
			t.Fatal(attributeName + " Attribute object missing getMandatoryAttributesFunction")
		}
		if (getProfileTypesFunction == nil){
			t.Fatal(attributeName + " Attribute object missing getProfileTypesFunction")
		}
		if (getIsCanonicalFunction == nil){
			t.Fatal(attributeName + " Attribute object missing getIsCanonicalFunction")
		}
		if (checkValueFunction == nil){
			t.Fatal(attributeName + " Attribute object missing checkValueFunction")
		}
		

		if (len(profileVersionsList) != 1){
			t.Fatal(attributeName + " Attribute object contains invalid profileVersionsList.")
		}
		supportedProfileVersion := profileVersionsList[0]

		if (supportedProfileVersion != 1){
			t.Fatal(attributeName + " Attribute object contains invalid profileVersionsList.")
		}

		_, exists := attributeIdentifiersMap[attributeIdentifier]
		if (exists == true){
			attributeIdentifierString := helpers.ConvertIntToString(attributeIdentifier)
			t.Fatal("Duplicate attribute identifier found: " + attributeIdentifierString)
		}
		attributeIdentifiersMap[attributeIdentifier] = struct{}{}
	
		_, exists = attributeNamesMap[attributeName]
		if (exists == true){
			t.Fatal("Duplicate attribute name found: " + attributeName)
		}
		attributeNamesMap[attributeName] = struct{}{}


		mandatoryAttributesList, err := getMandatoryAttributesFunction(1)
		if (err != nil){
			t.Fatal(attributeName + " Get Mandatory attributes function failed: " + err.Error())
		}

		containsDuplicates, _ := helpers.CheckIfListContainsDuplicates(mandatoryAttributesList)
		if (containsDuplicates == true){
			t.Fatal(attributeName + " Mandatory attributes list contains duplicates.")
		}

		for _, attributeString := range mandatoryAttributesList{
			allMandatoryAttributesMap[attributeString] = struct{}{}
		}

		profileTypesList, err := getProfileTypesFunction(1)
		if (err != nil){
			t.Fatal(attributeName + " GetProfileTypes function failed: " + err.Error())
		}

		if (len(profileTypesList) == 0 || len(profileTypesList) > 3){
			t.Fatal(attributeName + " GetProfileTypes function returning invalid list.")
		}

		// We use this map to detect duplicates
		profileTypesMap := make(map[string]struct{})
		
		for _, profileType := range profileTypesList{

			_, exists := profileTypesMap[profileType]
			if (exists == true){
				t.Fatal(attributeName + " GetProfileTypes function returning list with duplicates.")
			}
			profileTypesMap[profileType] = struct{}{}

			if (profileType != "Mate" && profileType != "Host" && profileType != "Moderator"){
				t.Fatal(attributeName + " GetProfileTypes function returning invalid profile type: " + profileType)
			}
		}

		isCanonical, err := getIsCanonicalFunction(1)
		if (err != nil){
			t.Fatal(attributeName + " GetIsCanonical failed to return:" + err.Error())
		}
		if (isCanonical != "Always" && isCanonical != "Sometimes" && isCanonical != "Never"){
			t.Fatal(attributeName + " GetIsCanonical returning invalid isCanonical: " + isCanonical)
		}
	}

	for mandatoryAttribute, _ := range allMandatoryAttributesMap{

		_, exists := attributeNamesMap[mandatoryAttribute]
		if (exists == false){
			t.Fatal("Mandatory attribute object not found: " + mandatoryAttribute)
		}	
	}
}


func TestProfileGeneticReferences(t *testing.T){

	// We use this to make sure all genetic attributes exist. 

	err := profileFormat.InitializeProfileFormatVariables()
	if (err != nil){
		t.Fatal("Failed to initialize profile format variables: " + err.Error())
	}

	attributeObjectsByNameMap, err := profileFormat.GetProfileAttributeObjectsByNameMap()
	if (err != nil){
		t.Fatal("GetProfileAttributeObjectsByNameMap failed: " + err.Error())
	}

	monogenicDiseases.InitializeMonogenicDiseaseVariables()

	monogenicDiseaseNamesList, err := monogenicDiseases.GetMonogenicDiseaseNamesList()
	if (err != nil){
		t.Fatal("GetMonogenicDiseaseNamesList failed: " + err.Error())
	}

	for _, monogenicDiseaseName := range monogenicDiseaseNamesList{

		diseaseNameWithUnderscores := strings.ReplaceAll(monogenicDiseaseName, " ", "_")

		probabilityOfPassingVariantAttributeName := "MonogenicDisease_" + diseaseNameWithUnderscores + "_ProbabilityOfPassingAVariant"

		variantsTestedAttributeName := "MonogenicDisease_" + diseaseNameWithUnderscores + "_NumberOfVariantsTested"

		_, attributeExists := attributeObjectsByNameMap[probabilityOfPassingVariantAttributeName]
		if (attributeExists == false){
			t.Fatal("Profile format missing attribute:" + probabilityOfPassingVariantAttributeName)
		}
	
		_, attributeExists = attributeObjectsByNameMap[variantsTestedAttributeName]
		if (attributeExists == false){
			t.Fatal("Profile format missing attribute:" + variantsTestedAttributeName)
		}
	}

	err = polygenicDiseases.InitializePolygenicDiseaseVariables()
	if (err != nil) {
		t.Fatal("InitializePolygenicDiseaseVariables failed: " + err.Error())
	}

	polygenicDiseaseObjectsList, err := polygenicDiseases.GetPolygenicDiseaseObjectsList()
	if (err != nil) {
		t.Fatal("GetPolygenicDiseaseObjectsList failed: " + err.Error())
	}

	for _, diseaseObject := range polygenicDiseaseObjectsList{

		diseaseLociList := diseaseObject.LociList

		for _, locusRSID := range diseaseLociList{

			locusRSIDString := helpers.ConvertInt64ToString(locusRSID)

			locusValueAttributeName := "LocusValue_rs" + locusRSIDString

			_, exists := attributeObjectsByNameMap[locusValueAttributeName]
			if (exists == false){
				t.Fatal("Profile format missing attribute: " + locusValueAttributeName)
			}		
		}
	}

	err = traits.InitializeTraitVariables()
	if (err != nil) {
		t.Fatal("InitializeTraitVariables failed: " + err.Error())
	}

	traitObjectsList, err := traits.GetTraitObjectsList()
	if (err != nil){
		t.Fatal("GetTraitObjectsList failed: " + err.Error())
	}

	// This map stores the rsID attributes which are missing from the profile format
	missingLociMap := make(map[string]struct{})

	for _, traitObject := range traitObjectsList{

		traitLociList := traitObject.LociList

		for _, rsID := range traitLociList{

			locusRSIDString := helpers.ConvertInt64ToString(rsID)

			locusValueAttributeName := "LocusValue_rs" + locusRSIDString

			_, exists := attributeObjectsByNameMap[locusValueAttributeName]
			if (exists == false){
				missingLociMap[locusValueAttributeName] = struct{}{}
			}
		}
	}

	if (len(missingLociMap) != 0){

		missingLociList := helpers.GetListOfMapKeys(missingLociMap)

		missingLociListString := strings.Join(missingLociList, ", ")

		t.Fatal("Profile format missing rsID attribute(s): " + missingLociListString)
	}
}



