'use strict';

const isStandardSyntaxRule = require('../utils/isStandardSyntaxRule');
const parseSelector = require('../utils/parseSelector');
const report = require('../utils/report');

module.exports = function (opts) {
	let hasFixed;

	opts.root.walkRules((rule) => {
		if (!isStandardSyntaxRule(rule)) {
			return;
		}

		hasFixed = false;
		const selector = rule.raws.selector ? rule.raws.selector.raw : rule.selector;

		const fixedSelector = parseSelector(selector, opts.result, rule, (selectorTree) => {
			selectorTree.walkCombinators((node) => {
				// Ignore spaced descendant combinator
				if (/\s/.test(node.value)) {
					return;
				}

				// Check the exist of node in prev of the combinator.
				// in case some that aren't the first begin with combinators (nesting syntax)
				if (opts.locationType === 'before' && !node.prev()) {
					return;
				}

				const parentParentNode = node.parent && node.parent.parent;

				// Ignore pseudo-classes selector like `.foo:nth-child(2n + 1) {}`
				if (parentParentNode && parentParentNode.type === 'pseudo') {
					return;
				}

				const sourceIndex = node.sourceIndex;
				const index =
					node.value.length > 1 && opts.locationType === 'before'
						? sourceIndex
						: sourceIndex + node.value.length - 1;

				check(selector, node, index, rule, sourceIndex);
			});
		});

		if (hasFixed) {
			if (!rule.raws.selector) {
				rule.selector = fixedSelector;
			} else {
				rule.raws.selector.raw = fixedSelector;
			}
		}
	});

	function check(source, combinator, index, node, sourceIndex) {
		opts.locationChecker({
			source,
			index,
			errTarget: combinator.value,
			err: (m) => {
				if (opts.fix && opts.fix(combinator)) {
					hasFixed = true;

					return;
				}

				report({
					message: m,
					node,
					index: sourceIndex,
					result: opts.result,
					ruleName: opts.checkedRuleName,
				});
			},
		});
	}
};
