text: "use strict";
  
  const _ = require("lodash");
  const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule");
  const parseSelector = require("../../utils/parseSelector");
  const report = require("../../utils/report");
  const ruleMessages = require("../../utils/ruleMessages");
  const styleSearch = require("style-search");
  const validateOptions = require("../../utils/validateOptions");
  
  const ruleName = "selector-attribute-brackets-space-inside";
  
  const messages = ruleMessages(ruleName, {
    expectedOpening: 'Expected single space after "["',
    rejectedOpening: 'Unexpected whitespace after "["',
    expectedClosing: 'Expected single space before "]"',
    rejectedClosing: 'Unexpected whitespace before "]"'
  });
  
  const rule = function(expectation, options, context) {
    return (root, result) => {
      const validOptions = validateOptions(result, ruleName, {
        actual: expectation,
        possible: ["always", "never"]
      });
      if (!validOptions) {
        return;
      }
  
      root.walkRules(rule => {
        if (!isStandardSyntaxRule(rule)) {
          return;
        }
        if (rule.selector.indexOf("[") === -1) {
          return;
        }
  
        const selector = rule.raws.selector
          ? rule.raws.selector.raw
          : rule.selector;
  
        let hasFixed;
        const fixedSelector = parseSelector(
          selector,
          result,
          rule,
          selectorTree => {
            selectorTree.walkAttributes(attributeNode => {
              const attributeSelectorString = attributeNode.toString();
  
              styleSearch(
                { source: attributeSelectorString, target: "[" },
                match => {
                  const nextCharIsSpace =
                    attributeSelectorString[match.startIndex + 1] === " ";
                  const index = attributeNode.sourceIndex + match.startIndex + 1;
                  if (nextCharIsSpace && expectation === "never") {
                    if (context.fix) {
                      hasFixed = true;
                      fixBefore(attributeNode);
                      return;
                    }
                    complain(messages.rejectedOpening, index);
                  }
                  if (!nextCharIsSpace && expectation === "always") {
                    if (context.fix) {
                      hasFixed = true;
                      fixBefore(attributeNode);
                      return;
                    }
                    complain(messages.expectedOpening, index);
                  }
                }
              );
  
              styleSearch(
                { source: attributeSelectorString, target: "]" },
                match => {
                  const prevCharIsSpace =
                    attributeSelectorString[match.startIndex - 1] === " ";
                  const index = attributeNode.sourceIndex + match.startIndex - 1;
                  if (prevCharIsSpace && expectation === "never") {
                    if (context.fix) {
                      hasFixed = true;
                      fixAfter(attributeNode);
                      return;
                    }
                    complain(messages.rejectedClosing, index);
                  }
                  if (!prevCharIsSpace && expectation === "always") {
                    if (context.fix) {
                      hasFixed = true;
                      fixAfter(attributeNode);
                      return;
                    }
                    complain(messages.expectedClosing, index);
                  }
                }
              );
            });
          }
        );
  
        if (hasFixed) {
          if (!rule.raws.selector) {
            rule.selector = fixedSelector;
          } else {
            rule.raws.selector.raw = fixedSelector;
          }
        }
  
        function complain(message, index) {
          report({
            message,
            index,
            result,
            ruleName,
            node: rule
          });
        }
      });
    };
  
    function fixBefore(attributeNode) {
      const rawAttrBefore = _.get(attributeNode, "raws.spaces.attribute.before");
      const { attrBefore, setAttrBefore } = rawAttrBefore
        ? {
            attrBefore: rawAttrBefore,
            setAttrBefore(fixed) {
              attributeNode.raws.spaces.attribute.before = fixed;
            }
          }
        : {
            attrBefore: _.get(attributeNode, "spaces.attribute.before", ""),
            setAttrBefore(fixed) {
              _.set(attributeNode, "spaces.attribute.before", fixed);
            }
          };
  
      if (expectation === "always") {
        setAttrBefore(attrBefore.replace(/^\s*/, " "));
      } else if (expectation === "never") {
        setAttrBefore(attrBefore.replace(/^\s*/, ""));
      }
    }
  
    function fixAfter(attributeNode) {
      let key;
  
      if (attributeNode.operator) {
        if (attributeNode.insensitive) {
          key = "insensitive";
        } else {
          key = "value";
        }
      } else {
        key = "attribute";
      }
      const rawAfter = _.get(attributeNode, `raws.spaces.${key}.after`);
      const { after, setAfter } = rawAfter
        ? {
            after: rawAfter,
            setAfter(fixed) {
              attributeNode.raws.spaces[key].after = fixed;
            }
          }
        : {
            after: _.get(attributeNode, `spaces.${key}.after`, ""),
            setAfter(fixed) {
              _.set(attributeNode, `spaces.${key}.after`, fixed);
            }
          };
  
      if (expectation === "always") {
        setAfter(after.replace(/\s*$/, " "));
      } else if (expectation === "never") {
        setAfter(after.replace(/\s*$/, ""));
      }
    }
  };
  
  rule.ruleName = ruleName;
  rule.messages = messages;
  module.exports = rule;
  