'use strict';

var utils = require('../utils.js');
var utils$1 = require('@typescript-eslint/utils');
var astUtils = require('@typescript-eslint/utils/ast-utils');
require('eslint-visitor-keys');
require('espree');
require('estraverse');

var _baseRule = utils.createRule({
  name: "space-infix-ops",
  package: "js",
  meta: {
    type: "layout",
    docs: {
      description: "Require spacing around infix operators"
    },
    fixable: "whitespace",
    schema: [
      {
        type: "object",
        properties: {
          int32Hint: {
            type: "boolean",
            default: false
          }
        },
        additionalProperties: false
      }
    ],
    messages: {
      missingSpace: "Operator '{{operator}}' must be spaced."
    }
  },
  create(context) {
    const int32Hint = context.options[0] ? context.options[0].int32Hint === true : false;
    const sourceCode = context.sourceCode;
    function getFirstNonSpacedToken(left, right, op) {
      const operator = sourceCode.getFirstTokenBetween(left, right, (token) => token.value === op);
      const prev = sourceCode.getTokenBefore(operator);
      const next = sourceCode.getTokenAfter(operator);
      if (!sourceCode.isSpaceBetween(prev, operator) || !sourceCode.isSpaceBetween(operator, next))
        return operator;
      return null;
    }
    function report(mainNode, culpritToken) {
      context.report({
        node: mainNode,
        loc: culpritToken.loc,
        messageId: "missingSpace",
        data: {
          operator: culpritToken.value
        },
        fix(fixer) {
          const previousToken = sourceCode.getTokenBefore(culpritToken);
          const afterToken = sourceCode.getTokenAfter(culpritToken);
          let fixString = "";
          if (culpritToken.range[0] - previousToken.range[1] === 0)
            fixString = " ";
          fixString += culpritToken.value;
          if (afterToken.range[0] - culpritToken.range[1] === 0)
            fixString += " ";
          return fixer.replaceText(culpritToken, fixString);
        }
      });
    }
    function checkBinary(node) {
      const leftNode = "typeAnnotation" in node.left && node.left.typeAnnotation ? node.left.typeAnnotation : node.left;
      const rightNode = node.right;
      const operator = "operator" in node && node.operator ? node.operator : "=";
      const nonSpacedNode = getFirstNonSpacedToken(leftNode, rightNode, operator);
      if (nonSpacedNode) {
        if (!(int32Hint && sourceCode.getText(node).endsWith("|0")))
          report(node, nonSpacedNode);
      }
    }
    function checkConditional(node) {
      const nonSpacedConsequentNode = getFirstNonSpacedToken(node.test, node.consequent, "?");
      const nonSpacedAlternateNode = getFirstNonSpacedToken(node.consequent, node.alternate, ":");
      if (nonSpacedConsequentNode)
        report(node, nonSpacedConsequentNode);
      if (nonSpacedAlternateNode)
        report(node, nonSpacedAlternateNode);
    }
    function checkVar(node) {
      const leftNode = node.id.typeAnnotation ? node.id.typeAnnotation : node.id;
      const rightNode = node.init;
      if (rightNode) {
        const nonSpacedNode = getFirstNonSpacedToken(leftNode, rightNode, "=");
        if (nonSpacedNode)
          report(node, nonSpacedNode);
      }
    }
    return {
      AssignmentExpression: checkBinary,
      AssignmentPattern: checkBinary,
      BinaryExpression: checkBinary,
      LogicalExpression: checkBinary,
      ConditionalExpression: checkConditional,
      VariableDeclarator: checkVar,
      // TODO: Stage 3: Overridden by ts version, can delete directly
      PropertyDefinition(node) {
        if (!node.value)
          return;
        const operatorToken = sourceCode.getTokenBefore(node.value, utils.isEqToken);
        const leftToken = sourceCode.getTokenBefore(operatorToken);
        const rightToken = sourceCode.getTokenAfter(operatorToken);
        if (!sourceCode.isSpaceBetween(leftToken, operatorToken) || !sourceCode.isSpaceBetween(operatorToken, rightToken)) {
          report(node, operatorToken);
        }
      }
    };
  }
});

const baseRule = /* @__PURE__ */ utils.castRuleModule(_baseRule);
const UNIONS = ["|", "&"];
var spaceInfixOps = utils.createRule({
  name: "space-infix-ops",
  package: "ts",
  meta: {
    type: "layout",
    docs: {
      description: "Require spacing around infix operators"
    },
    fixable: baseRule.meta.fixable,
    hasSuggestions: baseRule.meta.hasSuggestions,
    schema: baseRule.meta.schema,
    messages: baseRule.meta.messages
  },
  defaultOptions: [
    {
      int32Hint: false
    }
  ],
  create(context) {
    const rules = baseRule.create(context);
    const sourceCode = context.sourceCode;
    function report(node, operator) {
      context.report({
        node,
        loc: operator.loc,
        messageId: "missingSpace",
        data: {
          operator: operator.value
        },
        fix(fixer) {
          const previousToken = sourceCode.getTokenBefore(operator);
          const afterToken = sourceCode.getTokenAfter(operator);
          let fixString = "";
          if (operator.range[0] - previousToken.range[1] === 0)
            fixString = " ";
          fixString += operator.value;
          if (afterToken.range[0] - operator.range[1] === 0)
            fixString += " ";
          return fixer.replaceText(operator, fixString);
        }
      });
    }
    function isSpaceChar(token) {
      return token.type === utils$1.AST_TOKEN_TYPES.Punctuator && /^[=?:]$/.test(token.value);
    }
    function checkAndReportAssignmentSpace(node, leftNode, rightNode) {
      if (!rightNode || !leftNode)
        return;
      const operator = sourceCode.getFirstTokenBetween(
        leftNode,
        rightNode,
        isSpaceChar
      );
      const prev = sourceCode.getTokenBefore(operator);
      const next = sourceCode.getTokenAfter(operator);
      if (!sourceCode.isSpaceBetween(prev, operator) || !sourceCode.isSpaceBetween(operator, next)) {
        report(node, operator);
      }
    }
    function checkForEnumAssignmentSpace(node) {
      checkAndReportAssignmentSpace(node, node.id, node.initializer);
    }
    function checkForPropertyDefinitionAssignmentSpace(node) {
      const leftNode = node.optional && !node.typeAnnotation ? sourceCode.getTokenAfter(node.key) : node.typeAnnotation ?? node.key;
      checkAndReportAssignmentSpace(node, leftNode, node.value);
    }
    function checkForTypeAnnotationSpace(typeAnnotation) {
      const types = typeAnnotation.types;
      types.forEach((type) => {
        const skipFunctionParenthesis = type.type === utils$1.AST_NODE_TYPES.TSFunctionType ? astUtils.isNotOpeningParenToken : 0;
        const operator = sourceCode.getTokenBefore(
          type,
          skipFunctionParenthesis
        );
        if (operator != null && UNIONS.includes(operator.value)) {
          const prev = sourceCode.getTokenBefore(operator);
          const next = sourceCode.getTokenAfter(operator);
          if (!sourceCode.isSpaceBetween(prev, operator) || !sourceCode.isSpaceBetween(operator, next)) {
            report(typeAnnotation, operator);
          }
        }
      });
    }
    function checkForTypeAliasAssignment(node) {
      checkAndReportAssignmentSpace(
        node,
        node.typeParameters ?? node.id,
        node.typeAnnotation
      );
    }
    function checkForTypeConditional(node) {
      checkAndReportAssignmentSpace(node, node.extendsType, node.trueType);
      checkAndReportAssignmentSpace(node, node.trueType, node.falseType);
    }
    return {
      ...rules,
      TSEnumMember: checkForEnumAssignmentSpace,
      PropertyDefinition: checkForPropertyDefinitionAssignmentSpace,
      TSTypeAliasDeclaration: checkForTypeAliasAssignment,
      TSUnionType: checkForTypeAnnotationSpace,
      TSIntersectionType: checkForTypeAnnotationSpace,
      TSConditionalType: checkForTypeConditional
    };
  }
});

module.exports = spaceInfixOps;
