test_that("returns the correct linting", {
  ops <- c(
    "+",
    "-",
    "~",
    "=",
    "==",
    "!=",
    "<=",
    ">=",
    "<-",
    ":=",
    "<<-",
    "<",
    ">",
    "->",
    "->>",
    "%%",
    "/",
    "*",
    "|",
    "||",
    "&",
    "&&",
    "%>%",
    "%Anything%",
    "%+%",
    NULL
  )

  linter <- infix_spaces_linter()
  lint_msg <- rex::rex("Put spaces around all infix operators.")

  expect_lint("blah", NULL, linter)

  for (op in ops) {
    expect_lint(paste0("1 ", op, " 2"), NULL, linter)
    expect_lint(paste0("1 ", op, "\n2"), NULL, linter)
    expect_lint(paste0("1 ", op, "\n 2"), NULL, linter)

    expect_lint(paste0("1", op, "2"), lint_msg, linter)

    # unary plus and minus can have no space before them
    if (!op %in% ops[1L:2L]) {
      expect_lint(paste0("1 ", op, "2"), lint_msg, linter)
    }

    expect_lint(paste0("1", op, " 2"), lint_msg, linter)
  }

  expect_lint("b <- 2E+4", NULL, linter)
  expect_lint("a <- 1e-3", NULL, linter)
  expect_lint("a[-1]", NULL, linter)
  expect_lint("a[-1 + 1]", NULL, linter)
  expect_lint("a[1 + -1]", NULL, linter)

  expect_lint("fun(a=1)", lint_msg, linter)
})

test_that("The three `=` are all linted", {
  linter <- infix_spaces_linter()
  lint_msg <- rex::rex("Put spaces around all infix operators.")

  # EQ_ASSIGN in the parse data
  expect_lint("a=1", lint_msg, linter)
  # EQ_FORMALS in the parse data
  expect_lint("foo <- function(x=1) {}", lint_msg, linter)
  # EQ_SUB in the parse data
  expect_lint("foo(x=1)", lint_msg, linter)
})

test_that("exclude_operators works", {
  lint_msg <- rex::rex("Put spaces around all infix operators.")

  expect_lint("a+b", NULL, infix_spaces_linter(exclude_operators = "+"))
  expect_lint(
    trim_some("
      a+b
      a-b
    "),
    NULL,
    infix_spaces_linter(exclude_operators = c("+", "-"))
  )

  # operators match on text, not hidden node
  expect_lint("a<<-1", lint_msg, infix_spaces_linter(exclude_operators = "<-"))
  expect_lint("a<<-1", NULL, infix_spaces_linter(exclude_operators = "<<-"))
  expect_lint("a:=1", lint_msg, infix_spaces_linter(exclude_operators = "<-"))
  expect_lint("a:=1", NULL, infix_spaces_linter(exclude_operators = ":="))
  expect_lint("a->>1", lint_msg, infix_spaces_linter(exclude_operators = "->"))
  expect_lint("a->>1", NULL, infix_spaces_linter(exclude_operators = "->>"))
  expect_lint("a%any%1", NULL, infix_spaces_linter(exclude_operators = "%%"))
  expect_lint("function(a=1) { }", NULL, infix_spaces_linter(exclude_operators = "="))
  expect_lint("foo(a=1)", NULL, infix_spaces_linter(exclude_operators = "="))
})

# more tests specifically for assignment
test_that("assignment cases return the correct linting", {
  linter <- infix_spaces_linter()
  lint_msg <- rex::rex("Put spaces around all infix operators.")

  expect_lint("fun(blah =  1)", NULL, linter)

  expect_lint("blah <- 1", NULL, linter)
  expect_lint("blah = 1", NULL, linter)

  expect_lint("\"my  =  variable\" <- 42.0", NULL, linter)

  expect_lint("if (0 <  1) x <- 42L", NULL, linter)
  expect_lint(
    trim_some("
    if (0 < 1) {
      x <- 42L
    }"),
    NULL,
    linter
  )
  expect_lint("my = bad = variable = name <- 2.0", NULL, linter)

  expect_lint("blah<-  1", lint_msg, linter)
  expect_lint("blah  <-1", lint_msg, linter)
  expect_lint("blah=  1", lint_msg, linter)
  expect_lint("blah  =1", lint_msg, linter)
})

test_that("infix_spaces_linter can allow >1 spaces optionally", {
  expect_lint(
    "x  ~  1",
    rex::rex("Put exactly one space on each side of infix operators."),
    infix_spaces_linter(allow_multiple_spaces = FALSE)
  )
  expect_lint(
    "x  - 1",
    rex::rex("Put exactly one space on each side of infix operators."),
    infix_spaces_linter(allow_multiple_spaces = FALSE)
  )
  expect_lint(
    "x /  1",
    rex::rex("Put exactly one space on each side of infix operators."),
    infix_spaces_linter(allow_multiple_spaces = FALSE)
  )
})

test_that("exception for box::use()", {
  linter <- infix_spaces_linter()

  expect_lint("box::use(a/b)", NULL, linter)
  expect_lint("box::use(./a/b)", NULL, linter)
  expect_lint(
    trim_some("
      box::use(
        a,
        a/b,
        ../a,
        alias = a/b/c[xyz = abc, ...],
      )
    "),
    NULL,
    linter
  )
})

test_that("multi-line, multi-expression case is caught", {
  expect_lint(
    trim_some("
      x +
        y+
        z
    "),
    rex::rex("Put spaces around all infix operators."),
    infix_spaces_linter()
  )
})

test_that("Rules around missing arguments are respected", {
  linter <- infix_spaces_linter()
  lint_msg <- rex::rex("Put spaces around all infix operators.")

  expect_lint("switch(a = , b = 2)", NULL, linter)
  expect_lint("alist(missing_arg = )", NULL, linter)

  expect_lint("switch(a =, b = 2)", lint_msg, linter)
  expect_lint("alist(missing_arg =)", lint_msg, linter)
})

test_that("native pipe is supported", {
  skip_if_not_r_version("4.1.0")
  linter <- infix_spaces_linter()

  expect_lint("a |> foo()", NULL, linter)
  expect_lint("a|>foo()", rex::rex("Put spaces around all infix operators."), linter)
})

test_that("mixed unary & binary operators aren't mis-lint", {
  expect_lint(
    "-1-1",
    list(
      message = rex::rex("Put spaces around all infix operators."),
      column_number = 3L
    ),
    infix_spaces_linter()
  )
})

test_that("parse tags are accepted by exclude_operators", {
  expect_lint("sum(x, na.rm=TRUE)", NULL, infix_spaces_linter(exclude_operators = "EQ_SUB"))
  expect_lint("function(x, na.rm=TRUE) { }", NULL, infix_spaces_linter(exclude_operators = "EQ_FORMALS"))
  expect_lint("x=1", NULL, infix_spaces_linter(exclude_operators = "EQ_ASSIGN"))

  # uses parse_tag
  expect_lint("1+1", NULL, infix_spaces_linter(exclude_operators = "'+'"))

  # mixing
  text <- "x=function(a=foo(bar=1)) { }"
  col_assign <- list(column_number = 2L)
  col_formals <- list(column_number = 13L)
  col_sub <- list(column_number = 21L)
  expect_lint(text, NULL, infix_spaces_linter(exclude_operators = c("EQ_SUB", "EQ_FORMALS", "EQ_ASSIGN")))
  expect_lint(text, col_assign, infix_spaces_linter(exclude_operators = c("EQ_SUB", "EQ_FORMALS")))
  expect_lint(text, col_formals, infix_spaces_linter(exclude_operators = c("EQ_SUB", "EQ_ASSIGN")))
  expect_lint(text, col_sub, infix_spaces_linter(exclude_operators = c("EQ_FORMALS", "EQ_ASSIGN")))
  expect_lint(text, list(col_assign, col_formals), infix_spaces_linter(exclude_operators = "EQ_SUB"))
  expect_lint(text, list(col_assign, col_sub), infix_spaces_linter(exclude_operators = "EQ_FORMALS"))
  expect_lint(text, list(col_formals, col_sub), infix_spaces_linter(exclude_operators = "EQ_ASSIGN"))
})
