import { AbilityAccessors } from '../Ability';
import { ClassAccessors } from '../Class';
import { HelperUtils } from '../Helper';
import { RaceAccessors } from '../Race';
import { getEntityId, getEntityKey, getEntityTypeId, getPrerequisites, getShouldExclude, getSubType, getType, getValue, } from './accessors';
import { PrerequisiteSubTypeEnum, PrerequisiteTypeEnum } from './constants';
/**
 *
 * @param prerequisiteGrouping
 * @param prerequisiteData
 */
export function validatePrerequisiteGrouping(prerequisiteGrouping, prerequisiteData) {
    if (prerequisiteGrouping === null) {
        return true;
    }
    let isValid = !prerequisiteGrouping.length;
    prerequisiteGrouping.forEach((prerequisiteGroup) => {
        if (isValid) {
            return;
        }
        let meetsGroupPrereqs = true;
        const prerequisites = getPrerequisites(prerequisiteGroup);
        prerequisites.forEach((prerequisite) => {
            if (!meetsGroupPrereqs) {
                return;
            }
            meetsGroupPrereqs = validatePrerequisite(prerequisite, prerequisiteData);
        });
        isValid = isValid || meetsGroupPrereqs;
    });
    return isValid;
}
/**
 *
 * @param prerequisite
 * @param prerequisiteData
 */
export function validatePrerequisite(prerequisite, prerequisiteData) {
    switch (getType(prerequisite)) {
        case PrerequisiteTypeEnum.ABILITY_SCORE:
            return validatePrerequisiteAbilityScore(prerequisite, prerequisiteData);
        case PrerequisiteTypeEnum.PROFICIENCY:
            return validatePrerequisiteProficiency(prerequisite, prerequisiteData);
        case PrerequisiteTypeEnum.SPECIES:
            return validatePrerequisiteRace(prerequisite, prerequisiteData);
        case PrerequisiteTypeEnum.SPECIES_OPTION:
            return validatePrerequisiteSubrace(prerequisite, prerequisiteData);
        case PrerequisiteTypeEnum.SIZE:
            return validatePrerequisiteSize(prerequisite, prerequisiteData);
        case PrerequisiteTypeEnum.LEVEL:
            return validatePrerequisiteLevel(prerequisite, prerequisiteData);
        case PrerequisiteTypeEnum.CLASS:
            return validatePrerequisiteClass(prerequisite, prerequisiteData);
        case PrerequisiteTypeEnum.FEAT:
            return validatePrerequisiteFeat(prerequisite, prerequisiteData);
        case PrerequisiteTypeEnum.CLASS_FEATURE:
            return validatePrerequisiteClassFeature(prerequisite, prerequisiteData);
        case PrerequisiteTypeEnum.CUSTOM_VALUE:
        default:
        // not defined
    }
    return true;
}
export function validatePrerequisiteClassFeature(prerequisite, prerequisiteData) {
    const shouldExclude = getShouldExclude(prerequisite);
    if (!prerequisite.entityId)
        return true;
    // check if class feature exists in the class feature lookup
    const hasClassFeature = Object.prototype.hasOwnProperty.call(prerequisiteData.classFeatureLookup, prerequisite.entityId);
    return shouldExclude ? !hasClassFeature : hasClassFeature;
}
/**
 *
 * @param prerequisite
 * @param prerequisiteData
 */
export function validatePrerequisiteLevel(prerequisite, prerequisiteData) {
    const shouldExclude = getShouldExclude(prerequisite);
    switch (getSubType(prerequisite)) {
        case PrerequisiteSubTypeEnum.CHARACTER_LEVEL:
            const value = getValue(prerequisite);
            // check if the character level is greater than or equal to the value
            const characterLevelBeatsValue = prerequisiteData.characterLevel >= (value || 0);
            return shouldExclude ? !characterLevelBeatsValue : characterLevelBeatsValue;
        default:
        // not implemented
    }
    return true;
}
/**
 *
 * @param prerequisite
 * @param prerequisiteData
 */
export function validatePrerequisiteAbilityScore(prerequisite, prerequisiteData) {
    const entityId = getEntityId(prerequisite);
    if (entityId === null) {
        return true;
    }
    const ability = prerequisiteData.abilityLookup[entityId];
    const abilityScore = AbilityAccessors.getScore(ability);
    if (abilityScore === null) {
        return true;
    }
    const value = getValue(prerequisite);
    if (value === null) {
        return true;
    }
    const shouldExclude = getShouldExclude(prerequisite);
    // if shouldExclude is true, we want to check if the ability score is less than the value
    if (shouldExclude) {
        return abilityScore < value;
    }
    // if shouldExclude is false, we want to check if the ability score is greater than or equal to the value
    return abilityScore >= value;
}
/**
 *
 * @param prerequisite
 * @param prerequisiteData
 */
export function validatePrerequisiteProficiency(prerequisite, prerequisiteData) {
    const shouldExclude = getShouldExclude(prerequisite);
    //check if the proficiency exists in the proficiency lookup
    const hasProficiency = prerequisiteData.proficiencyLookup.hasOwnProperty(getEntityKey(prerequisite));
    return shouldExclude ? !hasProficiency : hasProficiency;
}
/**
 *
 * @param prerequisite
 * @param prerequisiteData
 */
export function validatePrerequisiteRace(prerequisite, prerequisiteData) {
    if (!prerequisiteData.race) {
        return true;
    }
    const shouldExclude = getShouldExclude(prerequisite);
    // check if race is a base race
    if (RaceAccessors.getBaseRaceId(prerequisiteData.race) === getEntityId(prerequisite) &&
        RaceAccessors.getBaseRaceTypeId(prerequisiteData.race) === getEntityTypeId(prerequisite)) {
        return !shouldExclude;
    }
    // check if race is specific race
    return validatePrerequisiteEntityRace(prerequisite, prerequisiteData);
}
/**
 *
 * @param prerequisite
 * @param prerequisiteData
 */
export function validatePrerequisiteSubrace(prerequisite, prerequisiteData) {
    if (!prerequisiteData.race) {
        return true;
    }
    // check if race is specific race
    return validatePrerequisiteEntityRace(prerequisite, prerequisiteData);
}
/**
 *
 * @param prerequisite
 * @param prerequisiteData
 */
function validatePrerequisiteEntityRace(prerequisite, prerequisiteData) {
    if (!prerequisiteData.race) {
        return true;
    }
    const shouldExclude = getShouldExclude(prerequisite);
    // check if the race type and id match the prerequisite
    const isMatch = RaceAccessors.getEntityRaceId(prerequisiteData.race) === getEntityId(prerequisite) &&
        RaceAccessors.getEntityRaceTypeId(prerequisiteData.race) === getEntityTypeId(prerequisite);
    return shouldExclude ? !isMatch : isMatch;
}
/**
 *
 * @param prerequisite
 * @param prerequisiteData
 */
export function validatePrerequisiteSize(prerequisite, prerequisiteData) {
    if (!prerequisiteData.race) {
        return true;
    }
    const sizeInfo = RaceAccessors.getSizeInfo(prerequisiteData.race);
    if (!sizeInfo) {
        return true;
    }
    const shouldExclude = getShouldExclude(prerequisite);
    // check if size matches the prerequisite size
    const isMatch = sizeInfo.id === getEntityId(prerequisite) && sizeInfo.entityTypeId === getEntityTypeId(prerequisite);
    return shouldExclude ? !isMatch : isMatch;
}
/**
 *
 * @param prerequisite
 * @param prerequisiteData
 */
export function validatePrerequisiteClass(prerequisite, prerequisiteData) {
    const entityId = getEntityId(prerequisite);
    if (entityId === null) {
        return true;
    }
    const shouldExclude = getShouldExclude(prerequisite);
    // check if class exists in the base class lookup
    const hasClass = Object.values(prerequisiteData.baseClassLookup).some((charClass) => ClassAccessors.getId(charClass) === entityId);
    return shouldExclude ? !hasClass : hasClass;
}
/**
 *
 * @param prerequisite
 * @param prerequisiteData
 */
export function validatePrerequisiteFeat(prerequisite, prerequisiteData) {
    const entityId = getEntityId(prerequisite);
    if (entityId === null) {
        return true;
    }
    const shouldExclude = getShouldExclude(prerequisite);
    // check if feat exists in the feat lookup
    const hasFeat = !!HelperUtils.lookupDataOrFallback(prerequisiteData.featLookup, entityId);
    return shouldExclude ? !hasFeat : hasFeat;
}
