/**
 *
 * @param {[{result: Number}]|String} rolledDice either the result of a roll or a roll-formula
 * @param {Number} value the value of this dice roll
 * @param {Number} mod modifier to the value
 * @param {[number]} werte an array of values that the dice roll is compared against
 * @param {{getRollData:() => {} }} owner the actor of this roll that is required when rolledDice is a roll-formula
 * @param {Number} lowerThreshold this is the threshold against a critical success is counted against
 * @param {Number} upperThreshold this is the threshold against a critical fumble is counted against
 * @param {Number} countToMeisterlich amount of critical success are needed for the dice roll to be a critical success
 * @param {Number} countToPatzer amount of critical fumbles are needed for the dice roll to be a critical failure
 * @param {*} rollObject the foundry Roll Object
 * @returns {{tap: number, meisterlich: boolean, patzer: boolean, evaluated: Roll.Evaluated}}
 */

const evaluateRoll = async (rolledDice, {
    value,
    mod = 0,
    werte = [],
    owner,
    lowerThreshold = 1,
    upperThreshold = 20,
    countToMeisterlich = 3,
    countToPatzer = 3,
}, rollObject = Roll) => {
    let tap = value + mod;
    let meisterlichCounter = 0;
    let patzerCounter = 0;
    let failCounter = 0;
    let evaluated = null

    // normalise counts
    if (countToMeisterlich > werte.length) {
        countToMeisterlich = werte.length
    }
    if (countToPatzer > werte.length) {
        countToPatzer = werte.length
    }

    if (typeof rolledDice == "string") { // we need to roll it ourselves
        let roll1 = new rollObject(rolledDice, owner.getRollData());
        evaluated = await roll1.evaluate()
        rolledDice = evaluated.terms[0].results
    }


    if (tap < 0) { // increases rolledDice by |tap| (as this defacto lowers the target value)
        rolledDice = rolledDice.map(({result, active}) => {
            return {
                result: result - tap,
                active
            }
        })
        tap = 0 // and then reset tap to 0 as we applied the reduction
    }


    rolledDice.forEach((rolledDie, index) => {
        if (tap < 0 && rolledDie.result > werte[index]) {
            tap -= rolledDie.result - werte[index];
            if (tap < 0) { // konnte nicht vollständig ausgeglichen werden
                failCounter++;
            }
        } else if (rolledDie.result > werte[index]) { // taw ist bereits aufgebraucht und wert kann nicht ausgeglichen werden
            tap -= rolledDie.result - werte[index];
            failCounter++;
        }
        if (rolledDie.result <= lowerThreshold) meisterlichCounter++;
        if (rolledDie.result >= upperThreshold) patzerCounter++;
    })

    if (meisterlichCounter >= countToMeisterlich) {
        tap = value
    }

    if (tap > value) { // can't have more tap than value as result
        tap = value
    }

    if (tap === 0) {
        tap = 1
    }

    return {
        tap,
        meisterlich: meisterlichCounter === countToMeisterlich,
        patzer: patzerCounter === countToPatzer,
        evaluated
    }
}

export {
    evaluateRoll
}