import sortBy from 'lodash/sortBy';
import { AbilityAccessors } from '../Ability';
import { FormatUtils } from '../Format';
import { getEntityId, getFriendlySubtypeName, getPrerequisites, getSubType, getType, getValue, getShouldExclude, } from './accessors';
import { PrerequisiteSubTypeEnum, PrerequisiteTypeEnum } from './constants';
import { validatePrerequisite } from './validators';
/**
 *
 * @param prerequisite
 * @param prerequisiteData
 */
export function getPrerequisiteFailure(prerequisite, prerequisiteData) {
    switch (getType(prerequisite)) {
        case PrerequisiteTypeEnum.ABILITY_SCORE:
            return getPrerequisiteFailureAbilityScore(prerequisite, prerequisiteData);
        case PrerequisiteTypeEnum.PROFICIENCY:
            return getPrerequisiteFailureProficiency(prerequisite);
        case PrerequisiteTypeEnum.SPECIES:
            return getPrerequisiteFailureRace(prerequisite);
        case PrerequisiteTypeEnum.SIZE:
            return getPrerequisiteFailureSize(prerequisite);
        case PrerequisiteTypeEnum.SPECIES_OPTION:
            return getPrerequisiteFailureSubrace(prerequisite);
        case PrerequisiteTypeEnum.LEVEL:
            return getPrerequisiteFailureLevel(prerequisite);
        case PrerequisiteTypeEnum.CLASS:
            return getPrerequisiteFailureClass(prerequisite);
        case PrerequisiteTypeEnum.FEAT:
            return getPrerequisiteFailureFeat(prerequisite);
        case PrerequisiteTypeEnum.CLASS_FEATURE:
            return getPrerequisiteFailureClassFeature(prerequisite);
        case PrerequisiteTypeEnum.CUSTOM_VALUE:
        default:
        // not defined
    }
    return null;
}
/**
 *
 * @param prerequisite
 * @param prerequisiteData
 */
export function getPrerequisiteFailureLevel(prerequisite) {
    let requiredDescription = '';
    switch (getSubType(prerequisite)) {
        case PrerequisiteSubTypeEnum.CHARACTER_LEVEL:
            let value = getValue(prerequisite);
            value = value ? value : 1;
            requiredDescription = `${FormatUtils.ordinalize(value)} Level`;
            break;
        default:
        // not implemented
    }
    return {
        type: PrerequisiteTypeEnum.LEVEL,
        data: {
            requiredChoice: getFriendlySubtypeName(prerequisite),
            requiredDescription,
        },
        shouldExclude: !!getShouldExclude(prerequisite),
    };
}
/**
 *
 * @param prerequisite
 * @param prerequisiteData
 */
export function getPrerequisiteFailureAbilityScore(prerequisite, prerequisiteData) {
    let statKey = '';
    let currentAmount = null;
    const entityId = getEntityId(prerequisite);
    if (entityId !== null) {
        const ability = prerequisiteData.abilityLookup[entityId];
        statKey = AbilityAccessors.getStatKey(ability);
        currentAmount = AbilityAccessors.getScore(ability);
    }
    return {
        type: PrerequisiteTypeEnum.ABILITY_SCORE,
        data: {
            statKey,
            currentAmount,
            requiredValue: getValue(prerequisite),
            requiredChoice: getFriendlySubtypeName(prerequisite),
            requiredDescription: `${getFriendlySubtypeName(prerequisite)} ${getValue(prerequisite)}+`,
        },
        shouldExclude: !!getShouldExclude(prerequisite),
    };
}
/**
 *
 * @param prerequisite
 * @param prerequisiteData
 */
export function getPrerequisiteFailureProficiency(prerequisite) {
    return {
        type: PrerequisiteTypeEnum.PROFICIENCY,
        data: {
            requiredChoice: getFriendlySubtypeName(prerequisite),
            requiredDescription: `${getFriendlySubtypeName(prerequisite)} Proficiency`,
        },
        shouldExclude: !!getShouldExclude(prerequisite),
    };
}
/**
 *
 * @param prerequisite
 * @param prerequisiteData
 */
export function getPrerequisiteFailureRace(prerequisite) {
    return {
        type: PrerequisiteTypeEnum.SPECIES,
        data: {
            requiredChoice: getFriendlySubtypeName(prerequisite),
            requiredDescription: getFriendlySubtypeName(prerequisite),
        },
        shouldExclude: !!getShouldExclude(prerequisite),
    };
}
/**
 *
 * @param prerequisite
 * @param prerequisiteData
 */
export function getPrerequisiteFailureSubrace(prerequisite) {
    return {
        type: PrerequisiteTypeEnum.SPECIES_OPTION,
        data: {
            requiredChoice: getFriendlySubtypeName(prerequisite),
            requiredDescription: getFriendlySubtypeName(prerequisite),
        },
        shouldExclude: !!getShouldExclude(prerequisite),
    };
}
/**
 *
 * @param prerequisite
 * @param prerequisiteData
 */
export function getPrerequisiteFailureSize(prerequisite) {
    return {
        type: PrerequisiteTypeEnum.SIZE,
        data: {
            requiredChoice: getFriendlySubtypeName(prerequisite),
            requiredDescription: `${getFriendlySubtypeName(prerequisite)} Size`,
        },
        shouldExclude: !!getShouldExclude(prerequisite),
    };
}
/**
 *
 * @param prerequisite
 * @param prerequisiteData
 */
export function getPrerequisiteFailureClass(prerequisite) {
    return {
        type: PrerequisiteTypeEnum.CLASS,
        data: {
            requiredChoice: getFriendlySubtypeName(prerequisite),
            requiredDescription: `${getFriendlySubtypeName(prerequisite)}`,
        },
        shouldExclude: !!getShouldExclude(prerequisite),
    };
}
export const getPrerequisiteFailureClassFeature = (prerequisite) => ({
    type: PrerequisiteTypeEnum.CLASS_FEATURE,
    data: {
        requiredChoice: prerequisite.friendlySubTypeName,
        requiredDescription: prerequisite.friendlySubTypeName,
    },
    shouldExclude: !!getShouldExclude(prerequisite),
});
/**
 *
 * @param prerequisite
 * @param prerequisiteData
 */
export function getPrerequisiteFailureFeat(prerequisite) {
    return {
        type: PrerequisiteTypeEnum.FEAT,
        data: {
            requiredChoice: getFriendlySubtypeName(prerequisite),
            requiredDescription: `${getFriendlySubtypeName(prerequisite)}`,
        },
        shouldExclude: !!getShouldExclude(prerequisite),
    };
}
/**
 *
 * @param prerequisiteGrouping
 * @param prerequisiteData
 */
export function getPrerequisiteGroupingFailures(prerequisiteGrouping, prerequisiteData) {
    const groupingFailures = [];
    if (prerequisiteGrouping !== null) {
        prerequisiteGrouping.forEach((prerequisiteGroup) => {
            const groupFailures = [];
            const prerequisites = getPrerequisites(prerequisiteGroup);
            prerequisites.forEach((prerequisite) => {
                if (!validatePrerequisite(prerequisite, prerequisiteData)) {
                    const prereqFailure = getPrerequisiteFailure(prerequisite, prerequisiteData);
                    if (prereqFailure) {
                        groupFailures.push(prereqFailure);
                    }
                }
            });
            groupingFailures.push(groupFailures);
        });
    }
    return groupingFailures.filter((group) => group.length);
}
// Creates a string that summarizes the prerequisite failures for feats and multiclassing
export function getPrerequisiteFailuresText(prereqFailures) {
    //get all the prerequisite failures, sorted so that if every group failure is a missing prereq, they come first
    const failures = sortBy(prereqFailures, (failureGroup) => (failureGroup.every((f) => !f.shouldExclude) ? 0 : 1));
    // Create an intro string for the summary
    let intro = '';
    // Check if all failures are either missing or prohibited
    const hasOnlyMissingFailures = failures.every((failureGroup) => failureGroup.every((f) => !f.shouldExclude));
    const hasOnlyProhibitedFailures = failures.every((failureGroup) => failureGroup.every((f) => f.shouldExclude));
    // Determine the intro text based on the type of failures
    if (hasOnlyMissingFailures) {
        intro = 'Missing: ';
    }
    else if (hasOnlyProhibitedFailures) {
        intro = 'Prohibited by: ';
    }
    else {
        intro = 'Requires: ';
    }
    // Each failure is sorted so that prohibited failures come last, and then combined into a single string
    const combinedStrings = failures
        .map((failure) => sortBy(failure, (f) => (f.shouldExclude ? 1 : 0))
        .map((failureAnd) => {
        if (failureAnd.shouldExclude && !hasOnlyProhibitedFailures && !hasOnlyMissingFailures) {
            return `Prohibited by: ${failureAnd.data.requiredDescription}`;
        }
        return `${failureAnd.data.requiredDescription}`;
    })
        .reduce((acc, desc, idx) => {
        let connector = '';
        if (idx < failure.length - 2) {
            connector = ', ';
        }
        else if (idx < failure.length - 1) {
            connector = ' and ';
        }
        acc += `${desc}${connector}`;
        return acc;
    }, ''))
        .join(' or ');
    // return the intro text followed by the combined strings
    return `${intro}${combinedStrings}`;
}
