import * as _ from 'lodash-es';
function reduceExam(examStructure, reducer, initialValue) {
    function go(accumulator, element) {
        return element.childNodes.reduce(go, reducer(accumulator, element));
    }
    return go(initialValue, examStructure);
}
function validateAnswerLength(examStructure, answersById) {
    return reduceExam(examStructure, (errors, element) => {
        if (element.name === 'text-answer' && element.attributes.maxLength != null) {
            const maybeAnswer = answersById[element.attributes.questionId];
            if ((maybeAnswer === null || maybeAnswer === void 0 ? void 0 : maybeAnswer.type) === 'richText' || (maybeAnswer === null || maybeAnswer === void 0 ? void 0 : maybeAnswer.type) === 'text') {
                return maybeAnswer.characterCount > element.attributes.maxLength
                    ? [
                        ...errors,
                        {
                            type: 'AnswerTooLong',
                            displayNumber: element.attributes.displayNumber,
                            characterCount: maybeAnswer.characterCount,
                            elementType: 'question'
                        }
                    ]
                    : errors;
            }
            return errors;
        }
        else {
            return errors;
        }
    }, []);
}
function validateExtraAnswers(examStructure, answersById) {
    // A recursive function that that calculates the answer count at each level
    // (answerCount) and carries around an array of extra answers
    function go(element) {
        var _a;
        const displayNumber = element.name === 'exam' ? '' : element.attributes.displayNumber;
        switch (element.name) {
            case 'exam':
            case 'section':
            case 'question': {
                const children = element.childNodes.map(go);
                const childrenWithAnswers = children.filter(v => v.answerCount > 0);
                const maxAnswers = (_a = element.attributes.maxAnswers) !== null && _a !== void 0 ? _a : Infinity;
                // Currently, min-answers exists only at section level.
                const minAnswers = element.name === 'section' ? element.attributes.minAnswers : undefined;
                // At exam level, answer count is the sum of answer counts of each
                // section. At section and question levels, answer count is the number
                // of child questions that have _any_ answers.
                const answerCount = element.name === 'exam'
                    ? childrenWithAnswers.reduce((acc, section) => acc + section.answerCount, 0)
                    : childrenWithAnswers.length;
                const childExtraAnswers = _.flatMap(children, v => v.extraAnswers);
                // If there are extra answers on the current level, we prepend them to
                // the extra answers array, so it is ordered naturally in document
                // order.
                const extraAnswers = answerCount > maxAnswers
                    ? [
                        {
                            type: 'ExtraAnswer',
                            elementType: element.name,
                            displayNumber,
                            childQuestions: children.map(v => v.displayNumber),
                            maxAnswers,
                            minAnswers
                        },
                        ...childExtraAnswers
                    ]
                    : childExtraAnswers;
                return { answerCount, displayNumber, extraAnswers };
            }
            case 'text-answer':
            case 'audio-answer':
            case 'scored-text-answer':
            case 'choice-answer':
            case 'dnd-answer':
            case 'dropdown-answer': {
                const maybeAnswer = answersById[element.attributes.questionId];
                // An answer whose value is '' is considered to be empty, so we ignore
                // them.
                const isValidAnswer = (maybeAnswer === null || maybeAnswer === void 0 ? void 0 : maybeAnswer.value.length) > 0;
                return { answerCount: isValidAnswer ? 1 : 0, displayNumber, extraAnswers: [] };
            }
        }
    }
    return go(examStructure).extraAnswers;
}
export function validateAnswers(examStructure, answersById) {
    return _.flatMap([validateAnswerLength, validateExtraAnswers], validationFn => validationFn(examStructure, answersById));
}
//# sourceMappingURL=validateAnswers.js.map