test_that("base_no_win includes all aggregates and window functions", {
  # All aggregates must be included in window functions
  expect_equal(setdiff(names(base_agg), names(base_win)), character())

  # All window functions all need to be in no_in
  expect_equal(setdiff(names(base_win), names(base_no_win)), character())
})

# mathematics --------------------------------------------------------

test_that("basic arithmetic is correct", {
  local_con(simulate_dbi())
  expect_equal(test_translate_sql(1 + 2), sql("1.0 + 2.0"))
  expect_equal(test_translate_sql(2 * 4), sql("2.0 * 4.0"))
  expect_equal(test_translate_sql(5 ^ 2), sql("POWER(5.0, 2.0)"))
  expect_equal(test_translate_sql(100L %% 3L), sql("100 % 3"))

  expect_error(test_translate_sql(100L %/% 3L), "not available")
})

test_that("small numbers aren't converted to 0", {
  local_con(simulate_dbi())
  expect_equal(test_translate_sql(1e-9), sql("1e-09"))
})

test_that("unary plus works with numbers", {
  local_con(simulate_dbi())
  expect_equal(test_translate_sql(+10L), sql("10"))
  expect_equal(test_translate_sql(x == +10), sql('`x` = 10.0'))
  expect_equal(test_translate_sql(x %in% c(+1L, 0L)), sql('`x` IN (1, 0)'))
})

test_that("unary plus works for non-numeric expressions", {
  local_con(simulate_dbi())
  expect_equal(test_translate_sql(+(1L + 2L)), sql("(1 + 2)"))
  expect_equal(test_translate_sql(mean(x, na.rm = TRUE), window = FALSE), sql('AVG(`x`)'))
})

test_that("unary minus flips sign of number", {
  local_con(simulate_dbi())
  expect_equal(test_translate_sql(-10L), sql("-10"))
  expect_equal(test_translate_sql(-10L + x), sql("-10 + `x`"))
  expect_equal(test_translate_sql(x == -10), sql('`x` = -10.0'))
  expect_equal(test_translate_sql(x %in% c(-1L, 0L)), sql('`x` IN (-1, 0)'))
})

test_that("unary minus wraps non-numeric expressions", {
  local_con(simulate_dbi())
  expect_equal(test_translate_sql(-(1L + 2L)), sql("-(1 + 2)"))
  expect_equal(test_translate_sql(-mean(x, na.rm = TRUE), window = FALSE), sql('-AVG(`x`)'))
})

test_that("binary minus subtracts", {
  local_con(simulate_dbi())
  expect_equal(test_translate_sql(1L - 10L), sql("1 - 10"))
})

test_that("log base comes first", {
  local_con(simulate_dbi())
  expect_equal(test_translate_sql(log(x, 10)), sql('LOG(10.0, `x`)'))
})

test_that("log becomes ln", {
  local_con(simulate_dbi())
  expect_equal(test_translate_sql(log(x)), sql('LN(`x`)'))
})

test_that("can translate subsetting", {
  local_con(simulate_dbi())
  expect_equal(test_translate_sql(a$b), sql("`a`.`b`"))
  expect_equal(test_translate_sql(a[["b"]]), sql("`a`.`b`"))
  expect_equal(test_translate_sql(f(a)[["b"]]), sql("f(`a`).`b`"))

  expect_equal(test_translate_sql(a[["b"]][[1]]), sql('`a`.`b`[1]'))
  expect_snapshot(error = TRUE, {
    test_translate_sql(a[[x]])
    test_translate_sql(a[[TRUE]])
  })
})

test_that("$ doesn't evaluate second argument", {
  y <- list(id = 1)

  expect_snapshot(lazy_frame(x = 1, y = 1) %>% filter(x == y$id))
  expect_snapshot(lazy_frame(x = 1) %>% filter(x == y$id))
})

test_that("useful error if $ used with inlined value", {
  y <- 1
  expect_snapshot(lazy_frame(x = 1) %>% filter(x == y$id), error = TRUE)
})

# window ------------------------------------------------------------------

test_that("lead and lag translate n to integers", {
  local_con(simulate_dbi())

  expect_equal(test_translate_sql(lead(x, 1)), sql("LEAD(`x`, 1, NULL) OVER ()"))
  expect_equal(test_translate_sql(lag(x, 1)), sql("LAG(`x`, 1, NULL) OVER ()"))
})

# strings -----------------------------------------------------------------

test_that("can translate case insensitive like", {
  local_con(simulate_dbi())
  test_translate_sql(str_like(x, "abc"))
  expect_snapshot(
    test_translate_sql(str_like(x, "abc", ignore_case = FALSE)),
    error = TRUE
  )
})

test_that("can translate nzchar", {
  local_con(simulate_dbi())
  expect_equal(test_translate_sql(nzchar(y)), sql("((`y` IS NULL) OR `y` != '')"))
  expect_equal(test_translate_sql(nzchar(y, TRUE)), sql("`y` != ''"))
})

# aggregates --------------------------------------------------------------

test_that("all and any translated correctly", {
  db <- memdb_frame(g = c(1, 1, 2, 2, 3, 3), x = c(0, 0, 0, 1, 1, 1))

  sum_all_g <- db %>%
    group_by(g) %>%
    summarise(all = all(x == 1, na.rm = TRUE)) %>%
    filter(all) %>%
    pull(g)
  expect_equal(sum_all_g, 3)

  sum_any_g <- db %>%
    group_by(g) %>%
    summarise(any = any(x == 1, na.rm = TRUE)) %>%
    filter(any) %>%
    pull(g)
  expect_equal(sum_any_g, c(2, 3))

  win_all_g <- db %>%
    group_by(g) %>%
    filter(all(x == 1, na.rm = TRUE)) %>%
    pull(g)
  expect_equal(win_all_g, c(3, 3))

  win_any_g <- db %>%
    group_by(g) %>%
    filter(any(x == 1, na.rm = TRUE)) %>%
    pull(g)
  expect_equal(win_any_g, c(2, 2, 3, 3))
})

# binary/bitwise ---------------------------------------------------------------

test_that("bitwise operations", {
  local_con(simulate_dbi())
  expect_equal(test_translate_sql(bitwNot(x)),        sql("~(`x`)"))
  expect_equal(test_translate_sql(bitwAnd(x, 128L)),  sql("`x` & 128"))
  expect_equal(test_translate_sql(bitwOr(x, 128L)),   sql("`x` | 128"))
  expect_equal(test_translate_sql(bitwXor(x, 128L)),  sql("`x` ^ 128"))
  expect_equal(test_translate_sql(bitwShiftL(x, 2L)), sql("`x` << 2"))
  expect_equal(test_translate_sql(bitwShiftR(x, 2L)), sql("`x` >> 2"))
})

test_that("default raw escapes translated correctly", {
  mf <- lazy_frame(x = "abc", con = simulate_sqlite())

  a <- blob::as_blob("abc")
  b <- blob::as_blob(as.raw(c(0x01, 0x02)))
  L <- c(a, b)

  expect_snapshot(mf %>% filter(x == a))
  expect_snapshot(mf %>% filter(x %in% L))

  qry <- mf %>% filter(x %in% !!L)
  expect_snapshot(qry)
})

# DDL ---------------------------------------------------------------------

test_that("DDL operations generate expected SQL", {
  con <- simulate_dbi()

  expect_snapshot(sql_table_analyze(con, in_schema("schema", "tbl")))
  expect_snapshot(sql_query_explain(con, sql("SELECT * FROM foo")))

  expect_snapshot(sql_query_wrap(con, ident("table")))
  expect_snapshot(sql_query_wrap(con, in_schema("schema", "tbl")))
  expect_snapshot(sql_query_wrap(con, sql("SELECT * FROM foo")))

  expect_snapshot(sql_table_index(con, in_schema("schema", "tbl"), c("a", "b")))
  expect_snapshot(sql_table_index(con, in_schema("schema", "tbl"), "c", unique = TRUE))

  expect_snapshot(sql_query_save(con, sql("SELECT * FROM foo"), in_schema("temp", "tbl")))
})
