/**
 * @fileoverview Enforce a new line after jsx elements and expressions.
 * @author Johnny Zabala
 */

'use strict';

const docsUrl = require('../util/docsUrl');

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

module.exports = {
  meta: {
    docs: {
      description: 'Enforce a new line after jsx elements and expressions',
      category: 'Stylistic Issues',
      recommended: false,
      url: docsUrl('jsx-newline')
    },
    fixable: 'code'
  },
  create(context) {
    const jsxElementParents = new Set();
    const sourceCode = context.getSourceCode();
    return {
      'Program:exit'() {
        jsxElementParents.forEach((parent) => {
          parent.children.forEach((element, index, elements) => {
            if (element.type === 'JSXElement' || element.type === 'JSXExpressionContainer') {
              const firstAdjacentSibling = elements[index + 1];
              const secondAdjacentSibling = elements[index + 2];
              if (
                firstAdjacentSibling
                && secondAdjacentSibling
                && (firstAdjacentSibling.type === 'Literal' || firstAdjacentSibling.type === 'JSXText')
                // Check adjacent sibling has the proper amount of newlines
                && !/\n\s*\n/.test(firstAdjacentSibling.value)
              ) {
                context.report({
                  node: secondAdjacentSibling,
                  message: 'JSX element should start in a new line',
                  fix(fixer) {
                    return fixer.replaceText(
                      firstAdjacentSibling,
                      // double the last newline.
                      sourceCode.getText(firstAdjacentSibling)
                        .replace(/(\n)(?!.*\1)/g, '\n\n')
                    );
                  }
                });
              }
            }
          });
        });
      },
      ':matches(JSXElement, JSXFragment) > :matches(JSXElement, JSXExpressionContainer)': (node) => {
        jsxElementParents.add(node.parent);
      }
    };
  }
};