# test-summary-pedmat-vismat-vispstat.R
# Basic tests for summary_pedmat, vismat, and vispstat

# --- summary_pedmat ---
test_that("summary_pedmat works on A matrix", {
  tp <- tidyped(simple_ped)
  A <- pedmat(tp, method = "A")
  s <- summary_pedmat(A)

  expect_s3_class(s, "summary.pedmat")
  expect_equal(s$method, "A")
  expect_false(s$compact)
  expect_equal(s$n_original, nrow(tp))
})

test_that("summary.pedmat S3 dispatch works", {
  tp <- tidyped(simple_ped)
  A <- pedmat(tp, method = "A")
  # Use summary_pedmat directly; generic summary() may dispatch to
  # summary.matrix before summary.pedmat depending on class order.
  s <- summary_pedmat(A)

  expect_s3_class(s, "summary.pedmat")
})

test_that("summary_pedmat errors on non-pedmat input", {
  expect_error(summary_pedmat(matrix(1:4, 2, 2)), "must be a pedmat")
})

# --- vismat ---
test_that("vismat produces a heatmap from pedmat object", {
  tp <- tidyped(simple_ped)
  A <- pedmat(tp, method = "A")

  # vismat returns a lattice/trellis object
  p <- vismat(A, type = "heatmap")
  expect_true(inherits(p, "trellis"))
})

test_that("vismat produces a histogram from pedmat object", {
  tp <- tidyped(simple_ped)
  A <- pedmat(tp, method = "A")

  p <- vismat(A, type = "histogram")
  expect_true(inherits(p, "trellis"))
})

test_that("vismat works with tidyped input", {
  tp <- tidyped(simple_ped)

  p <- vismat(tp, type = "heatmap")
  expect_true(inherits(p, "trellis"))
})

test_that("vismat reorder=FALSE preserves original order", {
  tp <- tidyped(small_ped)
  A <- pedmat(tp, method = "A")
  p <- vismat(A, reorder = FALSE)
  expect_true(inherits(p, "trellis"))
})

test_that("vismat ids subsets the matrix", {
  tp <- tidyped(small_ped)
  A <- pedmat(tp, method = "A")
  target <- rownames(as.matrix(A))[1:5]
  p <- vismat(A, ids = target)
  expect_true(inherits(p, "trellis"))
})

test_that("vismat ids errors on non-existent IDs", {
  tp <- tidyped(small_ped)
  A <- pedmat(tp, method = "A")
  expect_error(vismat(A, ids = c("NONEXIST_1", "NONEXIST_2")),
               "None of the specified")
})

test_that("vismat by groups by generation", {
  tp <- tidyped(small_ped)
  A <- pedmat(tp, method = "A")
  expect_message(
    p <- vismat(A, ped = tp, by = "Gen"),
    "Aggregating"
  )
  expect_true(inherits(p, "trellis"))
})

test_that("matrix-free pedigree grouping matches explicit A aggregation", {
  tp <- tidyped(small_ped)
  A <- as.matrix(pedmat(tp, method = "A", sparse = FALSE))
  groups <- as.character(tp$Gen)
  levels <- sort(unique(groups))
  sizes <- tabulate(factor(groups, levels = levels), nbins = length(levels))

  row_agg <- rowsum(A, groups, reorder = TRUE)
  expected_sum <- t(rowsum(t(row_agg), groups, reorder = TRUE))
  expected <- expected_sum / outer(sizes, sizes)

  actual <- aggregate_pedigree_by_group(tp, tp$Ind, "Gen")
  expect_equal(actual, expected, tolerance = 1e-12)
})

test_that("vismat grouped tidyped path does not construct A", {
  tp <- tidyped(small_ped)
  testthat::local_mocked_bindings(
    pedmat = function(...) stop("pedmat() must not be called"),
    .package = "visPedigree"
  )

  expect_s3_class(
    suppressMessages(vismat(tp, by = "Gen", reorder = FALSE)),
    "trellis"
  )
})

test_that("vismat by errors without ped", {
  tp <- tidyped(small_ped)
  A <- as.matrix(pedmat(tp, method = "A"))
  # Plain matrix has no ped attribute
  expect_error(vismat(A, by = "Gen"), "'ped' must be provided")
})

test_that("vismat grouping deprecated parameter works", {
  tp <- tidyped(small_ped)
  A <- pedmat(tp, method = "A")
  expect_warning(
    p <- vismat(A, ped = tp, grouping = "Gen"),
    "deprecated"
  )
  expect_true(inherits(p, "trellis"))
})

test_that("vismat works with plain matrix input", {
  tp <- tidyped(small_ped)
  A <- pedmat(tp, method = "A")
  A_dense <- as.matrix(A)
  class(A_dense) <- "matrix"  # ensure no pedmat class
  p <- vismat(A_dense)
  expect_true(inherits(p, "trellis"))
})

test_that("vismat labelcex controls label font size", {
  tp <- tidyped(small_ped)
  A <- pedmat(tp, method = "A")
  p <- vismat(A, labelcex = 0.3)
  expect_true(inherits(p, "trellis"))
})

test_that("vismat auto-expands compact pedmat", {
  tp <- tidyped(small_ped)
  A_compact <- pedmat(tp, method = "A", compact = TRUE)
  ci <- attr(A_compact, "call_info")

  # Should produce a message about expanding
  expect_message(
    p <- vismat(A_compact),
    "Expanding compact matrix"
  )
  expect_true(inherits(p, "trellis"))
})

test_that("vismat errors on unsupported type", {
  tp <- tidyped(small_ped)
  A <- pedmat(tp, method = "A")
  expect_error(vismat(A, type = "scatter"), "not supported")
})

# --- vispstat ---
test_that("vispstat produces ECG histogram", {
  tp <- tidyped(simple_ped)
  stats <- pedstats(tp)

  p <- vispstat(stats, type = "ecg")
  expect_true(inherits(p, "trellis"))
})

test_that("vispstat ECG supports FullGen metric", {
  tp <- tidyped(simple_ped)
  stats <- pedstats(tp)

  p <- vispstat(stats, type = "ecg", metric = "FullGen")
  expect_true(inherits(p, "trellis"))
})

test_that("vispstat errors on non-pedstats input", {
  expect_error(vispstat(list()), "must be a pedstats object")
})

test_that("plot.pedstats dispatches to vispstat for ecg", {
  tp <- tidyped(simple_ped)
  stats <- pedstats(tp)

  p <- plot(stats, type = "ecg")
  expect_true(inherits(p, "trellis"))
})

test_that("vispstat genint produces bar chart when gen_intervals exist", {
  tp <- tidyped(simple_ped)
  # Assign synthetic birth years so pedgenint can compute intervals
  tp$Year <- tp$Gen * 3 + 2000
  stats <- pedstats(tp, timevar = "Year")
  skip_if(is.null(stats$gen_intervals), "No generation intervals computed")

  p <- vispstat(stats, type = "genint")
  expect_true(inherits(p, "trellis"))
})

test_that("vispstat genint errors when gen_intervals is NULL", {
  tp <- tidyped(simple_ped)
  stats <- pedstats(tp, genint = FALSE)

  expect_error(vispstat(stats, type = "genint"),
               "No generation interval data found")
})

test_that("plot.pedstats dispatches genint correctly", {
  tp <- tidyped(simple_ped)
  tp$Year <- tp$Gen * 3 + 2000
  stats <- pedstats(tp, timevar = "Year")
  skip_if(is.null(stats$gen_intervals), "No generation intervals computed")

  p <- plot(stats, type = "genint")
  expect_true(inherits(p, "trellis"))
})

# --- vismat compact + by (fast aggregation path) ---
test_that("vismat compact+by=Gen matches expand-then-aggregate", {
  tp <- tidyped(small_ped)
  A_compact <- pedmat(tp, method = "A", compact = TRUE)
  A_full    <- pedmat(tp, method = "A", compact = FALSE)

  # Fast path: aggregate directly from compact
  agg_compact <- visPedigree:::aggregate_compact_by_group(
    A_compact, tp$Ind, "Gen", tp
  )

  # Reference: expand, then aggregate manually
  mat_exp <- as.matrix(expand_pedmat(A_compact))
  grp <- tp$Gen[match(rownames(mat_exp), tp$Ind)]
  grps <- sort(unique(grp))
  agg_ref <- matrix(0, length(grps), length(grps),
                    dimnames = list(grps, grps))
  for (i in seq_along(grps)) {
    for (j in i:length(grps)) {
      idx_i <- which(grp == grps[i])
      idx_j <- which(grp == grps[j])
      agg_ref[i, j] <- mean(mat_exp[idx_i, idx_j])
      agg_ref[j, i] <- agg_ref[i, j]
    }
  }

  expect_equal(agg_compact, agg_ref, tolerance = 1e-12)
})

test_that("vismat compact+by=Family matches expand-then-aggregate", {
  tp <- tidyped(small_ped)
  A_compact <- pedmat(tp, method = "A", compact = TRUE)

  # Focus on gen 3 (has 3 families)
  ids_g3 <- tp[Gen == 3, Ind]
  agg_compact <- visPedigree:::aggregate_compact_by_group(
    A_compact, ids_g3, "Family", tp
  )

  # Reference
  mat_exp <- as.matrix(expand_pedmat(A_compact))
  sub_ids <- ids_g3
  sub_mat <- mat_exp[sub_ids, sub_ids, drop = FALSE]
  grp <- tp$Family[match(sub_ids, tp$Ind)]
  grps <- sort(unique(grp[!is.na(grp)]))
  keep <- !is.na(grp)
  sub_mat <- sub_mat[keep, keep, drop = FALSE]
  grp <- grp[keep]
  agg_ref <- matrix(0, length(grps), length(grps),
                    dimnames = list(grps, grps))
  for (i in seq_along(grps)) {
    for (j in i:length(grps)) {
      idx_i <- which(grp == grps[i])
      idx_j <- which(grp == grps[j])
      agg_ref[i, j] <- mean(sub_mat[idx_i, idx_j])
      agg_ref[j, i] <- agg_ref[i, j]
    }
  }

  expect_equal(agg_compact, agg_ref, tolerance = 1e-12)
})

test_that("vismat compact+by produces valid trellis plot", {
  tp <- tidyped(small_ped)
  A_compact <- pedmat(tp, method = "A", compact = TRUE)

  expect_message(
    p <- vismat(A_compact, ped = tp, by = "Gen"),
    "Aggregating"
  )
  expect_true(inherits(p, "trellis"))
})
