import isEmpty_default from "../../../../../lodash-es/isEmpty.js";
import flatten_default from "../../../../../lodash-es/flatten.js";
import clone_default from "../../../../../lodash-es/clone.js";
import compact_default from "../../../../../lodash-es/compact.js";
import difference_default from "../../../../../lodash-es/difference.js";
import drop_default from "../../../../../lodash-es/drop.js";
import dropRight_default from "../../../../../lodash-es/dropRight.js";
import forEach_default from "../../../../../lodash-es/forEach.js";
import filter_default from "../../../../../lodash-es/filter.js";
import head_default from "../../../../../lodash-es/head.js";
import map_default from "../../../../../lodash-es/map.js";
import flatMap_default from "../../../../../lodash-es/flatMap.js";
import groupBy_default from "../../../../../lodash-es/groupBy.js";
import values_default from "../../../../../lodash-es/values.js";
import includes_default from "../../../../../lodash-es/includes.js";
import pickBy_default from "../../../../../lodash-es/pickBy.js";
import reduce_default from "../../../../../lodash-es/reduce.js";
import reject_default from "../../../../../lodash-es/reject.js";
import { Alternation, Alternative, NonTerminal, Option, Repetition, RepetitionMandatory, RepetitionMandatoryWithSeparator, RepetitionWithSeparator, Terminal } from "../../../../../@chevrotain/gast/lib/src/model.js";
import { GAstVisitor } from "../../../../../@chevrotain/gast/lib/src/visitor.js";
import { getProductionDslName, isOptionalProd } from "../../../../../@chevrotain/gast/lib/src/helpers.js";
import { tokenStructuredMatcher } from "../../scan/tokens.js";
import { nextPossibleTokensAfter } from "./interpreter.js";
import { containsPath, getLookaheadPathsForOptionalProd, getLookaheadPathsForOr, getProdType, isStrictPrefixOfPath } from "./lookahead.js";
import { ParserDefinitionErrorType } from "../parser/parser.js";
function validateLookahead(h) {
	return map_default(h.lookaheadStrategy.validate({
		rules: h.rules,
		tokenTypes: h.tokenTypes,
		grammarName: h.grammarName
	}), (h) => Object.assign({ type: ParserDefinitionErrorType.CUSTOM_LOOKAHEAD_VALIDATION }, h));
}
function validateGrammar(h, W, G, K) {
	let q = flatMap_default(h, (h) => validateDuplicateProductions(h, G)), J = checkTerminalAndNoneTerminalsNameSpace(h, W, G), Y = flatMap_default(h, (h) => validateTooManyAlts(h, G)), X = flatMap_default(h, (W) => validateRuleDoesNotAlreadyExist(W, h, K, G));
	return q.concat(J, Y, X);
}
function validateDuplicateProductions(h, W) {
	let G = new OccurrenceValidationCollector();
	h.accept(G);
	let K = G.allProductions;
	return map_default(values_default(pickBy_default(groupBy_default(K, identifyProductionForDuplicates), (h) => h.length > 1)), (G) => {
		let K = head_default(G), q = W.buildDuplicateFoundError(h, G), J = getProductionDslName(K), Y = {
			message: q,
			type: ParserDefinitionErrorType.DUPLICATE_PRODUCTIONS,
			ruleName: h.name,
			dslName: J,
			occurrence: K.idx
		}, X = getExtraProductionArgument(K);
		return X && (Y.parameter = X), Y;
	});
}
function identifyProductionForDuplicates(h) {
	return `${getProductionDslName(h)}_#_${h.idx}_#_${getExtraProductionArgument(h)}`;
}
function getExtraProductionArgument(h) {
	return h instanceof Terminal ? h.terminalType.name : h instanceof NonTerminal ? h.nonTerminalName : "";
}
var OccurrenceValidationCollector = class extends GAstVisitor {
	constructor() {
		super(...arguments), this.allProductions = [];
	}
	visitNonTerminal(h) {
		this.allProductions.push(h);
	}
	visitOption(h) {
		this.allProductions.push(h);
	}
	visitRepetitionWithSeparator(h) {
		this.allProductions.push(h);
	}
	visitRepetitionMandatory(h) {
		this.allProductions.push(h);
	}
	visitRepetitionMandatoryWithSeparator(h) {
		this.allProductions.push(h);
	}
	visitRepetition(h) {
		this.allProductions.push(h);
	}
	visitAlternation(h) {
		this.allProductions.push(h);
	}
	visitTerminal(h) {
		this.allProductions.push(h);
	}
};
function validateRuleDoesNotAlreadyExist(h, W, G, K) {
	let q = [];
	if (reduce_default(W, (W, G) => G.name === h.name ? W + 1 : W, 0) > 1) {
		let W = K.buildDuplicateRuleNameError({
			topLevelRule: h,
			grammarName: G
		});
		q.push({
			message: W,
			type: ParserDefinitionErrorType.DUPLICATE_RULE_NAME,
			ruleName: h.name
		});
	}
	return q;
}
function validateRuleIsOverridden(h, W, G) {
	let K = [], q;
	return includes_default(W, h) || (q = `Invalid rule override, rule: ->${h}<- cannot be overridden in the grammar: ->${G}<-as it is not defined in any of the super grammars `, K.push({
		message: q,
		type: ParserDefinitionErrorType.INVALID_RULE_OVERRIDE,
		ruleName: h
	})), K;
}
function validateNoLeftRecursion(W, K, J, Y = []) {
	let X = [], Z = getFirstNoneTerminal(K.definition);
	if (isEmpty_default(Z)) return [];
	{
		let h = W.name;
		includes_default(Z, W) && X.push({
			message: J.buildLeftRecursionError({
				topLevelRule: W,
				leftRecursionPath: Y
			}),
			type: ParserDefinitionErrorType.LEFT_RECURSION,
			ruleName: h
		});
		let K = flatMap_default(difference_default(Z, Y.concat([W])), (h) => {
			let K = clone_default(Y);
			return K.push(h), validateNoLeftRecursion(W, h, J, K);
		});
		return X.concat(K);
	}
}
function getFirstNoneTerminal(G) {
	let K = [];
	if (isEmpty_default(G)) return K;
	let q = head_default(G);
	/* istanbul ignore else */
	if (q instanceof NonTerminal) K.push(q.referencedRule);
	else if (q instanceof Alternative || q instanceof Option || q instanceof RepetitionMandatory || q instanceof RepetitionMandatoryWithSeparator || q instanceof RepetitionWithSeparator || q instanceof Repetition) K = K.concat(getFirstNoneTerminal(q.definition));
	else if (q instanceof Alternation) K = flatten_default(map_default(q.definition, (h) => getFirstNoneTerminal(h.definition)));
	else if (!(q instanceof Terminal)) throw Error("non exhaustive match");
	let Y = isOptionalProd(q), X = G.length > 1;
	if (Y && X) {
		let h = drop_default(G);
		return K.concat(getFirstNoneTerminal(h));
	} else return K;
}
var OrCollector = class extends GAstVisitor {
	constructor() {
		super(...arguments), this.alternations = [];
	}
	visitAlternation(h) {
		this.alternations.push(h);
	}
};
function validateEmptyOrAlternative(W, G) {
	let K = new OrCollector();
	W.accept(K);
	let q = K.alternations;
	return flatMap_default(q, (K) => flatMap_default(dropRight_default(K.definition), (q, J) => isEmpty_default(nextPossibleTokensAfter([q], [], tokenStructuredMatcher, 1)) ? [{
		message: G.buildEmptyAlternationError({
			topLevelRule: W,
			alternation: K,
			emptyChoiceIdx: J
		}),
		type: ParserDefinitionErrorType.NONE_LAST_EMPTY_ALT,
		ruleName: W.name,
		occurrence: K.idx,
		alternative: J + 1
	}] : []));
}
function validateAmbiguousAlternationAlternatives(h, W, G) {
	let K = new OrCollector();
	h.accept(K);
	let q = K.alternations;
	return q = reject_default(q, (h) => h.ignoreAmbiguities === !0), flatMap_default(q, (K) => {
		let q = K.idx, J = getLookaheadPathsForOr(q, h, K.maxLookahead || W, K), Y = checkAlternativesAmbiguities(J, K, h, G), X = checkPrefixAlternativesAmbiguities(J, K, h, G);
		return Y.concat(X);
	});
}
var RepetitionCollector = class extends GAstVisitor {
	constructor() {
		super(...arguments), this.allProductions = [];
	}
	visitRepetitionWithSeparator(h) {
		this.allProductions.push(h);
	}
	visitRepetitionMandatory(h) {
		this.allProductions.push(h);
	}
	visitRepetitionMandatoryWithSeparator(h) {
		this.allProductions.push(h);
	}
	visitRepetition(h) {
		this.allProductions.push(h);
	}
};
function validateTooManyAlts(h, W) {
	let G = new OrCollector();
	h.accept(G);
	let K = G.alternations;
	return flatMap_default(K, (G) => G.definition.length > 255 ? [{
		message: W.buildTooManyAlternativesError({
			topLevelRule: h,
			alternation: G
		}),
		type: ParserDefinitionErrorType.TOO_MANY_ALTS,
		ruleName: h.name,
		occurrence: G.idx
	}] : []);
}
function validateSomeNonEmptyLookaheadPath(G, K, q) {
	let J = [];
	return forEach_default(G, (G) => {
		let Y = new RepetitionCollector();
		G.accept(Y);
		let Z = Y.allProductions;
		forEach_default(Z, (Y) => {
			let X = getProdType(Y), Z = Y.maxLookahead || K, Q = Y.idx, $ = getLookaheadPathsForOptionalProd(Q, G, X, Z)[0];
			if (isEmpty_default(flatten_default($))) {
				let h = q.buildEmptyRepetitionError({
					topLevelRule: G,
					repetition: Y
				});
				J.push({
					message: h,
					type: ParserDefinitionErrorType.NO_NON_EMPTY_LOOKAHEAD,
					ruleName: G.name
				});
			}
		});
	}), J;
}
function checkAlternativesAmbiguities(h, W, G, K) {
	let q = [];
	return map_default(reduce_default(h, (G, K, J) => (W.definition[J].ignoreAmbiguities === !0 || forEach_default(K, (K) => {
		let Y = [J];
		forEach_default(h, (h, G) => {
			J !== G && containsPath(h, K) && W.definition[G].ignoreAmbiguities !== !0 && Y.push(G);
		}), Y.length > 1 && !containsPath(q, K) && (q.push(K), G.push({
			alts: Y,
			path: K
		}));
	}), G), []), (h) => {
		let q = map_default(h.alts, (h) => h + 1);
		return {
			message: K.buildAlternationAmbiguityError({
				topLevelRule: G,
				alternation: W,
				ambiguityIndices: q,
				prefixPath: h.path
			}),
			type: ParserDefinitionErrorType.AMBIGUOUS_ALTS,
			ruleName: G.name,
			occurrence: W.idx,
			alternatives: h.alts
		};
	});
}
function checkPrefixAlternativesAmbiguities(h, W, G, q) {
	let J = reduce_default(h, (h, W, G) => {
		let K = map_default(W, (h) => ({
			idx: G,
			path: h
		}));
		return h.concat(K);
	}, []);
	return compact_default(flatMap_default(J, (h) => {
		if (W.definition[h.idx].ignoreAmbiguities === !0) return [];
		let K = h.idx, Y = h.path;
		return map_default(filter_default(J, (h) => W.definition[h.idx].ignoreAmbiguities !== !0 && h.idx < K && isStrictPrefixOfPath(h.path, Y)), (h) => {
			let J = [h.idx + 1, K + 1], Y = W.idx === 0 ? "" : W.idx;
			return {
				message: q.buildAlternationPrefixAmbiguityError({
					topLevelRule: G,
					alternation: W,
					ambiguityIndices: J,
					prefixPath: h.path
				}),
				type: ParserDefinitionErrorType.AMBIGUOUS_PREFIX_ALTS,
				ruleName: G.name,
				occurrence: Y,
				alternatives: J
			};
		});
	}));
}
function checkTerminalAndNoneTerminalsNameSpace(h, W, G) {
	let K = [], q = map_default(W, (h) => h.name);
	return forEach_default(h, (h) => {
		let W = h.name;
		if (includes_default(q, W)) {
			let q = G.buildNamespaceConflictError(h);
			K.push({
				message: q,
				type: ParserDefinitionErrorType.CONFLICT_TOKENS_RULES_NAMESPACE,
				ruleName: W
			});
		}
	}), K;
}
export { validateAmbiguousAlternationAlternatives, validateEmptyOrAlternative, validateGrammar, validateLookahead, validateNoLeftRecursion, validateRuleIsOverridden, validateSomeNonEmptyLookaheadPath };
