'use strict';

var utils = require('./utils.js');
require('@typescript-eslint/utils');

var indentBinaryOps = utils.createRule({
  name: "indent-binary-ops",
  meta: {
    type: "layout",
    docs: {
      description: "Indentation for binary operators",
      recommended: "stylistic"
    },
    fixable: "whitespace",
    schema: [
      {
        oneOf: [
          {
            type: "integer",
            minimum: 0
          },
          {
            type: "string",
            enum: ["tab"]
          }
        ]
      }
    ],
    messages: {
      wrongIndentation: "Expected indentation of {{expected}}"
    }
  },
  defaultOptions: [2],
  create: (context, options) => {
    const { sourceCode } = context;
    const indentStr = options[0] === "tab" ? "	" : " ".repeat(options[0] ?? 2);
    const indentCache = /* @__PURE__ */ new Map();
    function getIndentOfLine(line) {
      if (indentCache.has(line))
        return indentCache.get(line);
      return sourceCode.lines[line - 1].match(/^\s*/)?.[0] ?? "";
    }
    function firstTokenOfLine(line) {
      return sourceCode.tokensAndComments.find((token) => token.loc.start.line === line);
    }
    function lastTokenOfLine(line) {
      return [...sourceCode.tokensAndComments].reverse().find((token) => token.loc.end.line === line);
    }
    function handler(node, right) {
      if (node.loc.start.line === node.loc.end.line)
        return;
      let tokenRight = sourceCode.getFirstToken(right);
      let tokenOperator = sourceCode.getTokenBefore(tokenRight);
      while (tokenOperator.value === "(") {
        tokenRight = tokenOperator;
        tokenOperator = sourceCode.getTokenBefore(tokenRight);
        if (tokenOperator.range[0] <= right.parent.range[0])
          return;
      }
      const tokenLeft = sourceCode.getTokenBefore(tokenOperator);
      const isMultiline = tokenRight.loc.start.line !== tokenLeft.loc.start.line;
      if (!isMultiline)
        return;
      const firstTokenOfLineLeft = firstTokenOfLine(tokenLeft.loc.start.line);
      const lastTokenOfLineLeft = lastTokenOfLine(tokenLeft.loc.start.line);
      const needAdditionIndent = firstTokenOfLineLeft?.type === "Keyword" && !["typeof", "instanceof", "this"].includes(firstTokenOfLineLeft.value) || firstTokenOfLineLeft?.type === "Identifier" && firstTokenOfLineLeft.value === "type" || [":", "[", "(", "<", "="].includes(lastTokenOfLineLeft?.value || "");
      const indentTarget = getIndentOfLine(tokenLeft.loc.start.line) + (needAdditionIndent ? indentStr : "");
      const indentRight = getIndentOfLine(tokenRight.loc.start.line);
      if (indentTarget !== indentRight) {
        const start = {
          line: tokenRight.loc.start.line,
          column: 0
        };
        const end = {
          line: tokenRight.loc.start.line,
          column: indentRight.length
        };
        context.report({
          loc: {
            start,
            end
          },
          messageId: "wrongIndentation",
          data: {
            expected: `${indentTarget.length} ${options[0] === "tab" ? "tab" : "space"}${indentTarget.length === 1 ? "" : "s"}`
          },
          fix(fixer) {
            return fixer.replaceTextRange(
              [sourceCode.getIndexFromLoc(start), sourceCode.getIndexFromLoc(end)],
              indentTarget
            );
          }
        });
        indentCache.set(tokenRight.loc.start.line, indentTarget);
      }
    }
    return {
      BinaryExpression(node) {
        handler(node, node.right);
      },
      LogicalExpression(node) {
        handler(node, node.right);
      },
      TSUnionType(node) {
        if (node.types.length > 1) {
          node.types.forEach((type) => {
            handler(node, type);
          });
        }
      },
      TSIntersectionType(node) {
        if (node.types.length > 1) {
          node.types.forEach((type) => {
            handler(node, type);
          });
        }
      }
    };
  }
});

module.exports = indentBinaryOps;
