source("helpers.R")
using("tinysnapshot")

if (!is_local) {
  exit_file("Run on Vincent's machine")
}

# vector row labels
set.seed(48103)
dat <- data.frame(
  label = c("a", "a", "a", "b", "b", "c", "a", "a"),
  x1 = rnorm(8),
  x2 = rnorm(8)
)
tab <- tt(dat[, 2:3]) |> group_tt(i = dat$label)
tab@output <- "markdown"
expect_snapshot_print(tab, label = "group-vector_row_labels.md")

# 3 level tests across formats
x <- mtcars[1:3, 1:5]
tab <- tt(x) |>
  group_tt(j = list("a" = 2:3, "b" = 4:5)) |>
  group_tt(j = list("c" = 1:2, "d" = 3:5)) |>
  group_tt(j = list("e" = 1:3, "f" = 4))
t <- expect_table(tab, formats = c("markdown", "latex", "typst"))
expect_snapshot_print(t[["markdown"]], "group-3level.md")
expect_snapshot_print(t[["latex"]], "group-3level.tex")
expect_snapshot_print(t[["typst"]], "group-3level.typ")

tab <- tt(mtcars[1:10, 1:5]) |>
  group_tt(
    i = list("Hello" = 3, "World" = 8),
    j = list("Foo" = 2:3, "Bar" = 4:5)
  )
expect_snapshot_print(print_html(tab), "group-html_tutorial_01.html")


# Issue #258: group_tt indices with 0 and duplicates
tab1 <- tt(head(iris)) |> group_tt(i = list("hello" = 1))
t1 <- expect_table(tab1)
expect_snapshot_print(t1[["latex"]], "group-issue258_01_latex.tex")
expect_snapshot_print(t1[["typst"]], "group-issue258_01_typst.typ")
expect_snapshot_print(t1[["markdown"]], "group-issue258_01_markdown.md")
expect_snapshot_print(t1[["html"]], "group-issue258_01_html.html")

tab2 <- tt(head(iris)) |> group_tt(i = list("hello" = 2, "world" = 2))
t2 <- expect_table(tab2)
expect_snapshot_print(t2[["latex"]], "group-issue258_02_latex.tex")
expect_snapshot_print(t2[["typst"]], "group-issue258_02_typst.typ")
expect_snapshot_print(t2[["markdown"]], "group-issue258_02_markdown.md")
expect_snapshot_print(t2[["html"]], "group-issue258_02_html.html")

# Issue #362: group_tt with duplicate column group labels in latex
tab <- data.frame(
  sub1 = 1,
  sub2 = 2,
  sub1 = 3,
  sub2 = 4,
  check.names = FALSE
) |>
  tt() |>
  group_tt(j = list(a = 1:2, a = 3:4))
tab@output <- "latex"
expect_snapshot_print(tab, "group-issue362_duplicate_colum_labels.tex")

# Issue #413: Automatic row groups fail if column is a factor
df <- mtcars |> head(10)
df <- df[order(df$am), ]
df$am <- factor(df$am)
tab <- tt(df) |> group_tt(i = df$am)
expect_inherits(tab, "tinytable")
expect_inherits(save_tt(tab, "markdown"), "character")


# delimiters in column names
# No delimiter
tab <- tt(mtcars |> head())
tab@output <- "markdown"
expect_snapshot_print(tab, "group-delim-no-delim.md")
tab <- tt(data.frame(
  A_id = 1,
  A_a1 = 2,
  A_a2 = "3",
  B_b1 = 4,
  B_b2 = 5,
  B_C = 6
)) |>
  group_tt(j = "_")
tab@output <- "markdown"
expect_snapshot_print(tab, "group-delim-all-delim.md")
tab <- tt(data.frame(
  id = 1,
  A_a1 = 2,
  A_a2 = "3",
  B_b1 = 4,
  B_b2 = 5,
  C = 6
)) |>
  group_tt(j = "_")
tab@output <- "markdown"
expect_snapshot_print(tab, "group-delim-some-delim.md")
tab <- tt(data.frame(
  id = 1,
  Axa1 = 2,
  Axa2 = "3",
  Bxb1 = 4,
  Bxb2 = 5,
  C = 6
)) |>
  group_tt(j = "x")
tab@output <- "markdown"
expect_snapshot_print(tab, "group-delim-x-delim.md")



# bug: same number of delimiters
dat <- data.frame(
  "A__D" = rnorm(3),
  "A_B_D" = rnorm(3),
  "A_B_" = rnorm(3),
  "_C_E" = rnorm(3),
  check.names = FALSE
)
tt(dat) |> group_tt(j = "_")


# Issue #466: group_tt without delimiter returns a valid table
tab <- tt(data.frame(Aid = 1, Aa1 = 2, Aa2 = "3", Bb1 = 4, Bb2 = 5, BC = 5)) |>
  group_tt(j = "_")
expect_inherits(tab, "tinytable")


# Issue #471: `style_tt()` group
set.seed(48103)
dat <- data.frame(
  label = c("a", "a", "a", "b", "b", "c", "a", "a"),
  x1 = rnorm(8),
  x2 = rnorm(8)
)
tab <- tt(dat[, 2:3]) |>
  group_tt(i = dat$label) |>
  style_tt(i = "groupi", color = "white", background = "black", align = "c")
tab@output <- "latex"
expect_snapshot_print(tab, label = "group_style_tt_group.tex")


# Issue #165: Column labels not centered, last row not styled because of row group offset
k = mtcars[1:5, 1:5]
k[1, 1] <- pi
tab <- tt(k) |>
  format_tt(num_fmt = "decimal", num_zero = TRUE, digits = 3) |>
  style_tt(i = 1:3, j = 1, bold = TRUE) |>
  style_tt(j = 2, italic = TRUE) |>
  group_tt(i = list("hello world" = 2), j = list("foo" = 1:2, "bar" = 3:4))
expect_snapshot_print(
  print_html(tab),
  "group-issue165_html_centering_style.html"
)


# informative errors
expect_error(
  tt(head(iris)) |> style_tt("groupj", color = "orange") |> print(),
  pattern = "No column grouping"
)

# informative errors
x <- head(iris)
colnames(x) <- NULL
expect_error(
  tt(x) |> style_tt("colnames", color = "orange") |> print(),
  pattern = "No column names found"
)
expect_error(
  tt(head(iris)) |> style_tt("groupj", color = "orange") |> print(),
  pattern = "No column grouping"
)


# i = "colnames" vs. "groupj"
tab <- tt(head(iris)) |>
  group_tt(j = list("Foo" = 2:3, "Bar" = 5)) |>
  group_tt(j = list("Hello" = 1:2, "World" = 4:5)) |>
  style_tt("groupj", color = "orange") |>
  style_tt("colnames", color = "teal")
tab <- expect_table(tab, "latex")[["latex"]]
expect_snapshot_print(tab, "group-style_tt_i_colnames_vs_groupj.tex")


# Issue #165: group_tt insert extra row at the bottom
k <- mtcars[1:4, 1:4]
tab <- tt(k) |>
  format_tt(num_fmt = "decimal", num_zero = TRUE, digits = 3) |>
  style_tt(i = 1:3, j = 1, bold = TRUE) |>
  style_tt(j = 2, italic = TRUE) |>
  group_tt(
    i = list("hello world" = 2),
    j = list("foo" = 1:2, "bar" = 3:4)
  )
t <- expect_table(tab)
expect_snapshot_print(t[["latex"]], "group-issue165_extra_row.tex")
expect_snapshot_print(t[["typst"]], "group-issue165_extra_row.typ")
expect_snapshot_print(t[["html"]], "group-issue165_extra_row.html")
expect_snapshot_print(t[["markdown"]], "group-issue165_extra_row.md")


# Issue #518: Multi-level delimiter grouping
# Test basic multi-level structure
tab <- data.frame(
  hi_hi_hi = 1,
  hi_ho_hu = 2
) |>
  tt() |>
  group_tt(j = "_")
t <- expect_table(tab)
expect_snapshot_print(t[["markdown"]], "group-multilevel-basic.md")
expect_snapshot_print(t[["latex"]], "group-multilevel-basic.tex")
expect_snapshot_print(t[["html"]], "group-multilevel-basic.html")
expect_snapshot_print(t[["typst"]], "group-multilevel-basic.typ")

# Mixed-level delimiter structure should error
tab_mixed_levels <- data.frame(
  A_X_1 = 1,
  A_X_2 = 2,
  A_Y_1 = 3,
  B_Z_1 = 4,
  B_Z_2 = 5,
  C_W = 6
)
expect_error(
  tab_mixed_levels |> tt() |> group_tt(j = "_"),
  "Each column name must have the same number of delimiters.",
  fixed = TRUE
)

# Test multi-level structure with empty fields.
# The trailing space in "A_B_ " preserves the delimiter count.
tab <- data.frame(
  "A__C" = 1,
  "A_B_C" = 2,
  "A_B_ " = 3,
  "_D_E" = 4,
  check.names = FALSE
) |>
  tt() |>
  group_tt(j = "_")
t <- expect_table(tab)
expect_snapshot_print(t[["markdown"]], "group-multilevel-empty.md")
expect_snapshot_print(t[["latex"]], "group-multilevel-empty.tex")
expect_snapshot_print(t[["html"]], "group-multilevel-empty.html")
expect_snapshot_print(t[["typst"]], "group-multilevel-empty.typ")


# Issue #543: Multi-level delimiter with shared level names spanning all columns
dat <- data.frame(
  "Strata group\nSex\nStrata level\nMale" = NA,
  "Strata group\nSex\nStrata level\nFemale" = NA,
  "Strata group\nAge\nStrata level\n<18" = NA,
  "Strata group\nAge\nStrata level\n>=18" = NA,
  check.names = FALSE
)
tab <- tt(dat) |> group_tt(j = "\n")
t <- expect_table(tab, "markdown")
expect_snapshot_print(t[["markdown"]], "group-shared-level-spanning.md")

# Indent markdown
dat <- do.call(rbind, by(iris, iris$Species, head, 3))
tab <- tt(dat) |>
  group_tt(i = dat$Species) |>
  style_tt(i = "~groupi", j = 1, indent = 3)
expect_snapshot_print(tab, "group-indent.md")

# nse
t <- mtcars |>
  sort_by(~am) |>
  tt() |>
  group_tt(i = am)
t <- expect_table(t)
expect_snapshot_print(t[["markdown"]], "group-nse.md")
expect_snapshot_print(t[["latex"]], "group-nse.tex")
expect_snapshot_print(t[["html"]], "group-nse.html")
expect_snapshot_print(t[["typst"]], "group-nse.typ")

# Issue #638: group_tt(j) after subset() placed column spans at wrong positions
dat <- mtcars[1:5, ] |>
  setNames(c("A", "B", "C", "D", "E", "F", "G", "H", "Total", "X", "Y"))
tab <- dat |>
  tt() |>
  group_tt(i = list("grp" = 3)) |>
  subset(select = -A) |>
  group_tt(j = list("AAA" = 3:4, "BBB" = 5:6))
t <- expect_table(tab)
expect_snapshot_print(t[["markdown"]], "group-subset_j-638.md")
expect_snapshot_print(t[["latex"]], "group-subset_j-638.tex")
expect_snapshot_print(t[["html"]], "group-subset_j-638.html")
expect_snapshot_print(t[["typst"]], "group-subset_j-638.typ")


# Unsorted list positions place labels identically to sorted input
x1 <- tt(mtcars[1:8, 1:3]) |> group_tt(i = list("B" = 5, "A" = 2))
x2 <- tt(mtcars[1:8, 1:3]) |> group_tt(i = list("A" = 2, "B" = 5))
expect_equal(unname(x1@group_index_i), c(2, 6))
expect_identical(save_tt(x1, "markdown"), save_tt(x2, "markdown"))
md <- strsplit(save_tt(x1, "markdown"), "\n")[[1]]
expect_true(min(grep("^\\| A", md)) < min(grep("^\\| B", md)))

# Character vector `i` combined with `j` keeps both row groups and column span
dat <- data.frame(x1 = 1:4, x2 = 5:8)
tab <- tt(dat) |> group_tt(i = c("g1", "g1", "g2", "g2"), j = list("span" = 1:2))
expect_equal(nrow(tab@group_data_i), 2)
expect_equal(nrow(tab@group_data_j), 1)
md <- save_tt(tab, "markdown")
expect_true(grepl("g1", md) && grepl("g2", md) && grepl("span", md))

# Documented i = list("label" = 0) inserts the label in the first row (synonym for 1)
t0 <- tt(head(iris, 3)) |> group_tt(i = list("first" = 0))
t1 <- tt(head(iris, 3)) |> group_tt(i = list("first" = 1))
expect_equal(unname(t0@group_index_i), 1)
expect_identical(save_tt(t0, "markdown"), save_tt(t1, "markdown"))
expect_false(grepl("NA", save_tt(t0, "markdown")))

# Stacked pure-i group_tt() calls work and match the i+j path
t1 <- tt(mtcars[1:8, 1:3]) |>
  group_tt(i = list("A" = 2)) |>
  group_tt(i = list("B" = 5))
t2 <- tt(mtcars[1:8, 1:3]) |>
  group_tt(i = list("A" = 2), j = list("g" = 1:2)) |>
  group_tt(i = list("B" = 5), j = list("h" = 1:2))
expect_equal(unname(t1@group_index_i), c(2, 5))
expect_equal(unname(t1@group_index_i), unname(t2@group_index_i))
expect_identical(t1@group_data_i, t2@group_data_i)

# Bare numeric `i` without a matrix `j` errors informatively
expect_error(
  tt(head(iris)) |> group_tt(i = c(2, 4)),
  pattern = "named list"
)

# Length-1 character `i` creates a single group covering the whole table
tab <- tt(head(iris, 3)) |> group_tt(i = "only")
expect_equal(nrow(tab@group_data_i), 1)
expect_equal(unname(tab@group_index_i), 1)
expect_true(grepl("only", save_tt(tab, "markdown")))

# NA labels in vector `i` produce a clear error message
expect_error(
  tt(head(iris, 4)) |> group_tt(i = c("a", "a", NA, "b")),
  pattern = "missing values"
)

# Non-contiguous column spans warn instead of silently swallowing columns
expect_warning(
  tt(head(iris)) |> group_tt(j = list("A" = c(1, 3))),
  pattern = "not contiguous"
)

# Duplicate column-group labels (documented example in ?group_tt)
tab <- tt(mtcars[1:3, 1:6]) |>
  group_tt(j = list("Hello" = 1:2, "World" = 3:4, "Hello" = 5:6))
md <- save_tt(tab, "markdown")
expect_equal(unname(lengths(gregexpr("Hello", md, fixed = TRUE))), 2)
expect_true(grepl("World", md, fixed = TRUE))
htm <- strip_random(save_tt(tab, "html"))
expect_equal(unname(lengths(gregexpr("Hello", htm, fixed = TRUE))), 2)
