"use strict";

const _ = require("lodash");
const blockString = require("../../utils/blockString");
const hasBlock = require("../../utils/hasBlock");
const hasEmptyBlock = require("../../utils/hasEmptyBlock");
const isSingleLineString = require("../../utils/isSingleLineString");
const report = require("../../utils/report");
const ruleMessages = require("../../utils/ruleMessages");
const validateOptions = require("../../utils/validateOptions");

const ruleName = "block-closing-brace-newline-before";

const messages = ruleMessages(ruleName, {
  expectedBefore: 'Expected newline before "}"',
  expectedBeforeMultiLine: 'Expected newline before "}" of a multi-line block',
  rejectedBeforeMultiLine:
    'Unexpected whitespace before "}" of a multi-line block'
});

const rule = function(expectation) {
  return (root, result) => {
    const validOptions = validateOptions(result, ruleName, {
      actual: expectation,
      possible: ["always", "always-multi-line", "never-multi-line"]
    });
    if (!validOptions) {
      return;
    }

    // Check both kinds of statements: rules and at-rules
    root.walkRules(check);
    root.walkAtRules(check);

    function check(statement) {
      // Return early if blockless or has empty block
      if (!hasBlock(statement) || hasEmptyBlock(statement)) {
        return;
      }

      // Ignore extra semicolon
      const after = (statement.raws.after || "").replace(/;+/, "");
      if (after === undefined) {
        return;
      }

      const blockIsMultiLine = !isSingleLineString(blockString(statement));
      const statementString = statement.toString();

      let index = statementString.length - 2;
      if (statementString[index - 1] === "\r") {
        index -= 1;
      }

      // We're really just checking whether a
      // newline *starts* the block's final space -- between
      // the last declaration and the closing brace. We can
      // ignore any other whitespace between them, because that
      // will be checked by the indentation rule.
      if (!_.startsWith(after, "\n") && !_.startsWith(after, "\r\n")) {
        if (expectation === "always") {
          complain(messages.expectedBefore);
        } else if (blockIsMultiLine && expectation === "always-multi-line") {
          complain(messages.expectedBeforeMultiLine);
        }
      }

      if (
        after !== "" &&
        blockIsMultiLine &&
        expectation === "never-multi-line"
      ) {
        complain(messages.rejectedBeforeMultiLine);
      }

      function complain(message) {
        report({
          message,
          result,
          ruleName,
          node: statement,
          index
        });
      }
    }
  };
};

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