

// myMateDesires provides functions to check if a peer passes a user's mate desires

package myMateDesires

// There is a difference between "Passing" and "Fulfilling"
// All users will pass desires for which a user has not enabled FilterAll
// A user Fulfills a desire if the user has responded and their attribute values fulfill our desires
// Checking if a profile fulfills a desire is only needed when calculating match scores

import "seekia/internal/desires/myLocalDesires"
import "seekia/internal/desires/mateDesires"

import "slices"
import "errors"


// This is aa list of all desire names we check when testing if a user passes our desires
// It does not include desires only used in criterias
var allMyDesiresList []string

// We use this function to initialize the allMyDesiresList
func init(){

	allDesiresList := mateDesires.GetAllDesiresList(false)

	allMyDesiresList = make([]string, 0)

	for _, desireName := range allDesiresList{

		if (desireName == "23andMe_AncestryComposition_Restrictive"){
			// This desire exists along with the non-restrictive version
			// We will only check either desire once, depending on if restrictive mode is enabled
			// Thus, we don't want to check it twice
			continue
		}
		desireIsCriteriaOnly := mateDesires.CheckIfDesireIsCriteriaOnly(desireName)
		if (desireIsCriteriaOnly == true){
			// These are desires that are only used for criteria, when querying hosts
			continue
		}

		allMyDesiresList = append(allMyDesiresList, desireName)
	}
}

func GetAllMyDesiresList(copyList bool)[]string{

	if (copyList == false){
		// List only needs to be copied if we are sorting and/or editing its elements afterwards
		return allMyDesiresList
	}

	listCopy := slices.Clone(allMyDesiresList)

	return listCopy
}

func CheckIfMateProfilePassesAllMyDesires(excludeADesire bool, desireToExclude string, getAnyProfileAttributeFunction func(string)(bool, int, string, error))(bool, error){

	for _, desireName := range allMyDesiresList{

		if (excludeADesire == true && desireName == desireToExclude){
			continue
		}

		userPassesDesire, err := CheckIfMateProfilePassesMyDesire(desireName, getAnyProfileAttributeFunction)
		if (err != nil) { return false, err }
		if (userPassesDesire == false){
			return false, nil
		}
	}

	return true, nil
}


// This function returns a map describing the desires the user passes
//Outputs:
//	-map[string]bool: Desire Name -> Profile passes desire bool
//	-error
func GetMateProfilePassesAllMyDesiresMap(getAnyProfileAttributeFunction func(string)(bool, int, string, error))(map[string]bool, error){

	passesAllMyDesiresMap := make(map[string]bool)

	for _, desireName := range allMyDesiresList{

		userPassesDesire, err := CheckIfMateProfilePassesMyDesire(desireName, getAnyProfileAttributeFunction)
		if (err != nil) { return nil, err }

		passesAllMyDesiresMap[desireName] = userPassesDesire
	}

	return passesAllMyDesiresMap, nil
}

// This function will see if a profile passes a user's desire
// This involves considering the FilterAll and RequireResponse settings
//Outputs:
//	-bool: User passes my desire 
//	-error
func CheckIfMateProfilePassesMyDesire(desireName string, getAnyProfileAttributeFunction func(string)(bool, int, string, error))(bool, error){

	desireAllowsResponseRequired, attributeNameToCheck := mateDesires.CheckIfDesireAllowsRequireResponse(desireName)
	if (desireAllowsResponseRequired == true){

		getRequireResponseBool := func()(bool, error){

			exists, currentResponseRequired, err := myLocalDesires.GetDesire(desireName + "_RequireResponse")
			if (err != nil) { return false, err }
			if (exists == true && currentResponseRequired == "Yes"){

				return true, nil
			}
			return false, nil
		}

		requireResponseBool, err := getRequireResponseBool()
		if (err != nil) { return false, err }

		profileContainsAttribute, _, _, err := getAnyProfileAttributeFunction(attributeNameToCheck)
		if (err != nil) { return false, err }
		if (profileContainsAttribute == false){

			// The profile is missing this attribute

			if (requireResponseBool == true){
				// User does not have a response, but we require one
				return false, nil
			}

			// User does not have a response, and we do not require one
			// User passes this desire
			return true, nil	
		}
	}

	exists, currentFilterAllSetting, err := myLocalDesires.GetDesire(desireName + "_FilterAll")
	if (err != nil) { return false, err }
	if (exists == false || currentFilterAllSetting != "Yes"){
		// We do not have filterAll enabled
		// All users will pass the desire
		return true, nil
	}

	myDesireExists, desireStatusIsKnown, desireIsFulfilled, err := CheckIfMateProfileFulfillsMyDesire(desireName, getAnyProfileAttributeFunction)
	if (err != nil) { return false, err }
	if (myDesireExists == false){
		// We have no desire specified
		// All users will pass this desire (they already fulfilled our RequireResponse desire, if we had one)
		return true, nil
	}
	if (desireStatusIsKnown == false){

		// This will only happen if the user did not respond
		// We already checked for this

		return false, errors.New("CheckIfMateProfileFulfillsMyDesire claims attribute response does not exist, but we already checked.")
	}

	return desireIsFulfilled, nil
}


//Ouputs:
//	-bool: My Desire exists
//	-bool: Desire status is known
//	-bool: Profile fullfills my desire (FilterAll and ResponseRequired are both ignored)
//	-error
func CheckIfMateProfileFulfillsMyDesire(inputDesireName string, getAnyProfileAttributeFunction func(string)(bool, int, string, error))(bool, bool, bool, error){

	getDesireName := func()(string, error){

		if (inputDesireName == "23andMe_AncestryComposition" || inputDesireName == "23andMe_AncestryComposition_Restrictive"){

			settingExists, restrictiveModeEnabled, err := myLocalDesires.GetDesire("23andMe_AncestryComposition_RestrictiveModeEnabled")
			if (err != nil) { return "", err }
			if (settingExists == true && restrictiveModeEnabled == "Yes"){
				return "23andMe_AncestryComposition_Restrictive", nil
			}
			return "23andMe_AncestryComposition", nil
		}

		return inputDesireName, nil
	}

	desireName, err := getDesireName()
	if (err != nil) { return false, false, false, err }

	getMyDesireFunction := func(inputDesire string)(bool, string, error){

		exists, desireValue, err := myLocalDesires.GetDesire(inputDesire)

		return exists, desireValue, err
	}

	anyDesireExists, desireIsValid, attributeResponseExists, desireIsFulfilled, err := mateDesires.CheckIfMateProfileFulfillsDesire(desireName, getMyDesireFunction, getAnyProfileAttributeFunction)
	if (err != nil) { return false, false, false, err }
	if (anyDesireExists == false){
		// We have no desire (ignoring RequireResponse, which may exist)
		return false, false, false, nil
	}
	if (desireIsValid == false){
		return false, false, false, errors.New("MyLocalDesires is malformed: Contains invalid " + desireName + " desire.")
	}
	if (attributeResponseExists == false){
		// User did not respond.
		return true, false, false, nil
	}

	return true, true, desireIsFulfilled, nil
}



