'use strict';

const isStandardSyntaxDeclaration = require('../../utils/isStandardSyntaxDeclaration');
const isStandardSyntaxProperty = require('../../utils/isStandardSyntaxProperty');
const postcss = require('postcss');
const report = require('../../utils/report');
const ruleMessages = require('../../utils/ruleMessages');
const validateOptions = require('../../utils/validateOptions');
const valueParser = require('postcss-value-parser');

const ruleName = 'shorthand-property-no-redundant-values';

const messages = ruleMessages(ruleName, {
	rejected: (unexpected, expected) =>
		`Unexpected longhand value '${unexpected}' instead of '${expected}'`,
});

const propertiesWithShorthandNotation = new Set([
	'margin',
	'padding',
	'border-color',
	'border-radius',
	'border-style',
	'border-width',
	'grid-gap',
]);

const ignoredCharacters = ['+', '*', '/', '(', ')', '$', '@', '--', 'var('];

function hasIgnoredCharacters(value) {
	return ignoredCharacters.some((char) => value.includes(char));
}

function isShorthandProperty(property) {
	return propertiesWithShorthandNotation.has(property);
}

function canCondense(top, right, bottom, left) {
	const lowerTop = top.toLowerCase();
	const lowerRight = right.toLowerCase();
	const lowerBottom = bottom && bottom.toLowerCase();
	const lowerLeft = left && left.toLowerCase();

	if (canCondenseToOneValue(lowerTop, lowerRight, lowerBottom, lowerLeft)) {
		return [top];
	}

	if (canCondenseToTwoValues(lowerTop, lowerRight, lowerBottom, lowerLeft)) {
		return [top, right];
	}

	if (canCondenseToThreeValues(lowerTop, lowerRight, lowerBottom, lowerLeft)) {
		return [top, right, bottom];
	}

	return [top, right, bottom, left];
}

function canCondenseToOneValue(top, right, bottom, left) {
	if (top !== right) {
		return false;
	}

	return (top === bottom && (bottom === left || !left)) || (!bottom && !left);
}

function canCondenseToTwoValues(top, right, bottom, left) {
	return (top === bottom && right === left) || (top === bottom && !left && top !== right);
}

function canCondenseToThreeValues(top, right, bottom, left) {
	return right === left;
}

function rule(actual, secondary, context) {
	return (root, result) => {
		const validOptions = validateOptions(result, ruleName, { actual });

		if (!validOptions) {
			return;
		}

		root.walkDecls((decl) => {
			if (!isStandardSyntaxDeclaration(decl) || !isStandardSyntaxProperty(decl.prop)) {
				return;
			}

			const prop = decl.prop;
			const value = decl.value;

			const normalizedProp = postcss.vendor.unprefixed(prop.toLowerCase());

			if (hasIgnoredCharacters(value) || !isShorthandProperty(normalizedProp)) {
				return;
			}

			const valuesToShorthand = [];

			valueParser(value).walk((valueNode) => {
				if (valueNode.type !== 'word') {
					return;
				}

				valuesToShorthand.push(valueParser.stringify(valueNode));
			});

			if (valuesToShorthand.length <= 1 || valuesToShorthand.length > 4) {
				return;
			}

			const shortestForm = canCondense(...valuesToShorthand);
			const shortestFormString = shortestForm.filter((value) => value).join(' ');
			const valuesFormString = valuesToShorthand.join(' ');

			if (shortestFormString.toLowerCase() === valuesFormString.toLowerCase()) {
				return;
			}

			if (context.fix) {
				decl.value = decl.value.replace(value, shortestFormString);
			} else {
				report({
					message: messages.rejected(value, shortestFormString),
					node: decl,
					result,
					ruleName,
				});
			}
		});
	};
}

rule.ruleName = ruleName;
rule.messages = messages;
module.exports = rule;
