// NOTICE: This file is generated by Rollup. To modify it,
// please instead edit the ESM counterpart and rebuild with Rollup (npm run build).
'use strict';

const typeGuards = require('../../utils/typeGuards.cjs');
const validateTypes = require('../../utils/validateTypes.cjs');
const isStandardSyntaxRule = require('../../utils/isStandardSyntaxRule.cjs');
const matchesStringOrRegExp = require('../../utils/matchesStringOrRegExp.cjs');
const report = require('../../utils/report.cjs');
const ruleMessages = require('../../utils/ruleMessages.cjs');
const validateOptions = require('../../utils/validateOptions.cjs');

const ruleName = 'rule-nesting-at-rule-required-list';

const messages = ruleMessages(ruleName, {
	expected: (...atRulePatterns) => {
		const patterns = atRulePatterns.map((pattern) => `"${pattern}"`).join(', ');

		return `Expected rule to be nested in an at-rule matching pattern(s): ${patterns}`;
	},
});

const meta = {
	url: 'https://stylelint.io/user-guide/rules/rule-nesting-at-rule-required-list',
};

/** @type {import('stylelint').CoreRules[ruleName]} */
const rule = (primary) => {
	return (root, result) => {
		// Validate that the primary option is a string, regex, or array of strings/regexes
		const validOptions = validateOptions(result, ruleName, {
			actual: primary,
			possible: [validateTypes.isString, validateTypes.isRegExp],
		});

		if (!validOptions) return;

		const requiredAtRules = [primary].flat();

		root.walkRules((ruleNode) => {
			if (!isStandardSyntaxRule(ruleNode)) return;

			// Skip if rule already has a required at-rule ancestor
			if (hasRequiredAtRuleAncestor(ruleNode, requiredAtRules)) return;

			report({
				message: messages.expected,
				messageArgs: requiredAtRules,
				node: ruleNode,
				result,
				ruleName,
			});
		});
	};
};

/**
 * Check if rule has a required at-rule ancestor
 * Traverses up the PostCSS AST tree from the rule to find
 * if any parent node is an at-rule that matches the required patterns
 * @param {import('postcss').Rule} ruleNode - The CSS rule to check
 * @param {Array<string | RegExp>} requiredAtRules - Patterns to match at-rule names against
 * @returns {boolean} True if rule has required at-rule ancestor, false otherwise
 */
function hasRequiredAtRuleAncestor(ruleNode, requiredAtRules) {
	let current = ruleNode.parent;

	while (current) {
		// Check if current node is an at-rule (e.g., @layer, @scope, @supports)
		if (typeGuards.isAtRule(current)) {
			const atRuleName = current.name;

			// Check if at-rule name matches any of the required patterns
			if (matchesStringOrRegExp(atRuleName, requiredAtRules)) return true;
		}

		if (typeGuards.isRoot(current)) break;

		current = current.parent;
	}

	return false;
}

rule.primaryOptionArray = true;
rule.ruleName = ruleName;
rule.messages = messages;
rule.meta = meta;

module.exports = rule;
