network_labels <- c(
  "Erdos-Renyi" = "Erdős–Rényi",
  "Barabasi-Albert" = "Barabási–Albert",
  "Watts-Strogatz" = "Watts–Strogatz"
)

density_labels <- c(
  "Sparse" = "Sparse — 20 edges (10.5%)",
  "Denser" = "Denser — 40 edges (21.1%)"
)

density_short_labels <- c(
  "Sparse" = "Sparse",
  "Denser" = "Denser"
)

metric_labels <- c(
  "degree" = "Binary degree",
  "strength" = "Strength",
  "closeness" = "Harmonic closeness",
  "betweenness" = "Betweenness"
)

target_labels <- c(
  "Low" = "Least central",
  "Average" = "Closest to mean",
  "High" = "Most central"
)

threshold_labels <- c(
  "0.10-0.20" = "Sensitive — U(.10, .20)",
  "0.20-0.40" = "Moderate — U(.20, .40)",
  "0.40-0.60" = "Elevated — U(.40, .60)",
  "0.60-0.80" = "Resilient — U(.60, .80)"
)

threshold_short_labels <- c(
  "0.10-0.20" = "Sensitive",
  "0.20-0.40" = "Moderate",
  "0.40-0.60" = "Elevated",
  "0.60-0.80" = "Resilient"
)

network_colors <- c(
  "Erdős–Rényi" = "#0072B2",
  "Barabási–Albert" = "#D55E00",
  "Watts–Strogatz" = "#009E73"
)

network_abbreviations <- c(
  "Erdos-Renyi" = "ER",
  "Barabasi-Albert" = "BA",
  "Watts-Strogatz" = "WS"
)

network_plotly_symbols <- c(
  "Erdos-Renyi" = "circle",
  "Barabasi-Albert" = "diamond",
  "Watts-Strogatz" = "square"
)

network_shapes <- c(
  "Erdős–Rényi" = 16,
  "Barabási–Albert" = 17,
  "Watts–Strogatz" = 15
)

density_linetypes <- c("Sparse" = "dashed", "Denser" = "solid")
target_shapes <- c("Low" = 21, "Average" = 23, "High" = 24)
target_colors <- c(
  "Low" = "#64748B",
  "Average" = "#0F766E",
  "High" = "#C2410C"
)

named_choices <- function(labels) {
  stats::setNames(names(labels), unname(labels))
}

format_number <- function(x, digits = 3L, suffix = "") {
  ifelse(
    is.na(x),
    "Not estimable",
    paste0(formatC(x, format = "f", digits = digits, big.mark = ","), suffix)
  )
}

format_count <- function(x) {
  ifelse(is.na(x), "—", formatC(x, format = "d", big.mark = ","))
}

add_display_columns <- function(data) {
  if ("network" %in% names(data)) {
    data$network_display <- unname(network_labels[data$network])
  }
  if ("density_condition" %in% names(data)) {
    data$density_display <- unname(density_labels[data$density_condition])
  }
  if ("metric" %in% names(data)) {
    data$metric_display <- unname(metric_labels[data$metric])
  }
  if ("metric_1" %in% names(data)) {
    data$metric_1_display <- unname(metric_labels[data$metric_1])
  }
  if ("metric_2" %in% names(data)) {
    data$metric_2_display <- unname(metric_labels[data$metric_2])
  }
  target_column <- intersect(c("target", "target_centrality"), names(data))
  if (length(target_column) == 1L) {
    data$target_display <- unname(target_labels[data[[target_column]]])
  }
  if ("threshold" %in% names(data)) {
    data$threshold_display <- unname(threshold_labels[data$threshold])
    data$threshold_short <- factor(
      unname(threshold_short_labels[data$threshold]),
      levels = unname(threshold_short_labels)
    )
  }
  data
}

filter_conditions <- function(data, input) {
  if ("density_condition" %in% names(data)) {
    data <- data[data$density_condition %in% input$density, , drop = FALSE]
  }
  if ("metric" %in% names(data)) {
    data <- data[data$metric %in% input$metric, , drop = FALSE]
  }
  if ("threshold" %in% names(data)) {
    data <- data[data$threshold %in% input$threshold, , drop = FALSE]
  }
  if ("target" %in% names(data) && !is.null(input$target) && length(input$target) > 0L) {
    data <- data[data$target %in% input$target, , drop = FALSE]
  }
  if ("target_centrality" %in% names(data) && !is.null(input$target) && length(input$target) > 0L) {
    data <- data[data$target_centrality %in% input$target, , drop = FALSE]
  }
  data
}

validate_filter_selection <- function(input) {
  shiny::validate(
    shiny::need(length(input$density) > 0L, "Select at least one density."),
    shiny::need(length(input$metric) > 0L, "Select at least one centrality metric."),
    shiny::need(length(input$threshold) > 0L, "Select at least one threshold regime.")
  )
}

pretty_names <- function(data) {
  label_map <- c(
    density_condition = "Density",
    network = "Graph family",
    metric = "Centrality metric",
    target = "Initial target",
    target_centrality = "Initial target",
    threshold = "Threshold range",
    mean_final_active = "Mean final active",
    sd_final_active = "MC SD: final active",
    se_final_active = "MC SE: final active",
    ci95_lo_final_active = "95% MC CI: lower",
    ci95_hi_final_active = "95% MC CI: upper",
    mean_steps_unconditional = "Mean stopping steps (audit only)",
    sd_steps_unconditional = "MC SD: stopping steps (audit only)",
    se_steps_unconditional = "MC SE: stopping steps (audit only)",
    n_complete_failures = "Complete cascades",
    percent_complete_failures = "Complete cascades (%)",
    mean_steps_to_complete_failure = "Mean steps to total failure",
    sd_steps_to_complete_failure = "Conditional MC SD",
    se_steps_to_complete_failure = "Conditional MC SE",
    ci95_lo_steps_to_complete_failure = "95% conditional MC CI: lower",
    ci95_hi_steps_to_complete_failure = "95% conditional MC CI: upper",
    n_replications = "Replications",
    mean_high_low_difference = "Mean Delta_F",
    median_high_low_difference = "Median Delta_F",
    sd_high_low_difference = "MC SD: Delta_F",
    se_high_low_difference = "MC SE: Delta_F",
    ci95_lo_high_low_difference = "95% MC CI: lower",
    ci95_hi_high_low_difference = "95% MC CI: upper",
    percent_high_greater = "% Delta_F > 0",
    percent_equal = "% Delta_F = 0",
    percent_high_lower = "% Delta_F < 0",
    is_table2_max = "Largest family mean",
    is_table3_fastest = "Smallest conditional mean",
    estimate = "Mean",
    mc_sd = "MC SD",
    mc_se = "MC SE",
    ci95_lo = "95% MC CI: lower",
    ci95_hi = "95% MC CI: upper"
  )
  replacements <- label_map[names(data)]
  names(data)[!is.na(replacements)] <- unname(replacements[!is.na(replacements)])
  data
}

humanize_values <- function(data) {
  if ("network" %in% names(data)) data$network <- unname(network_labels[data$network])
  if ("density_condition" %in% names(data)) data$density_condition <- unname(density_labels[data$density_condition])
  if ("metric" %in% names(data)) data$metric <- unname(metric_labels[data$metric])
  if ("metric_1" %in% names(data)) data$metric_1 <- unname(metric_labels[data$metric_1])
  if ("metric_2" %in% names(data)) data$metric_2 <- unname(metric_labels[data$metric_2])
  if ("target" %in% names(data)) data$target <- unname(target_labels[data$target])
  if ("target_centrality" %in% names(data)) {
    data$target_centrality <- unname(target_labels[data$target_centrality])
  }
  if ("threshold" %in% names(data)) data$threshold <- unname(threshold_labels[data$threshold])
  data
}

table_options <- list(
  pageLength = 12,
  lengthMenu = list(c(12, 25, 50, -1), c("12", "25", "50", "All")),
  scrollX = TRUE,
  autoWidth = TRUE,
  searchHighlight = TRUE
)

render_searchable_table <- function(data, digits = 4L) {
  numeric_columns <- which(vapply(data, is.numeric, logical(1)))
  widget <- DT::datatable(
    data,
    rownames = FALSE,
    filter = "top",
    extensions = c("Buttons"),
    options = c(
      table_options,
      list(
        dom = "Bfrtip",
        buttons = c("copy", "csv"),
        columnDefs = list(list(className = "dt-right", targets = numeric_columns - 1L))
      )
    ),
    class = "compact stripe hover"
  )
  if (length(numeric_columns) > 0L) {
    widget <- DT::formatRound(widget, numeric_columns, digits = digits)
  }
  widget
}
