"use strict";

const declarationValueIndex = require("../../utils/declarationValueIndex");
const functionArgumentsSearch = require("../../utils/functionArgumentsSearch");
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 = "function-linear-gradient-no-nonstandard-direction";

const messages = ruleMessages(ruleName, {
  rejected: "Unexpected nonstandard direction"
});

function isStandardDirection(source, withToPrefix) {
  const regexp = withToPrefix
    ? /^to (top|left|bottom|right)(?: (top|left|bottom|right))?$/
    : /^(top|left|bottom|right)(?: (top|left|bottom|right))?$/;

  const matches = source.match(regexp);
  if (!matches) {
    return false;
  }
  if (matches.length === 2) {
    return true;
  }
  // Cannot repeat side-or-corner, e.g. "to top top"
  if (matches.length === 3 && matches[1] !== matches[2]) {
    return true;
  }
  return false;
}

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

    root.walkDecls(decl => {
      valueParser(decl.value).walk(valueNode => {
        if (valueNode.type !== "function") {
          return;
        }
        functionArgumentsSearch(
          valueParser.stringify(valueNode).toLowerCase(),
          "linear-gradient",
          (expression, expressionIndex) => {
            const firstArg = expression.split(",")[0].trim();

            // If the first character is a number, we can assume the user intends an angle
            if (/[\d.]/.test(firstArg[0])) {
              if (/^[\d.]+(?:deg|grad|rad|turn)$/.test(firstArg)) {
                return;
              }
              complain();
              return;
            }

            // The first argument may not be a direction: it may be an angle,
            // or a color stop (in which case user gets default direction, "to bottom")
            // cf. https://drafts.csswg.org/css-images-3/#linear-gradient-syntax
            if (!/left|right|top|bottom/.test(firstArg)) {
              return;
            }

            const withToPrefix = !postcss.vendor.prefix(valueNode.value);
            if (!isStandardDirection(firstArg, withToPrefix)) {
              complain();
              return;
            }

            function complain() {
              report({
                message: messages.rejected,
                node: decl,
                index:
                  declarationValueIndex(decl) +
                  valueNode.sourceIndex +
                  expressionIndex,
                result,
                ruleName
              });
            }
          }
        );
      });
    });
  };
};

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