#' Stratified multivariable regression
#'
#' Fits multivariable regression models within each stratum and returns a
#' unified wide table with one "Characteristic" column and, under bold
#' spanners for each stratum, model N, "Adjusted <effect>", and "p-value".
#'
#' If \code{adjust_for = NULL}, all \code{exposures} are included in one
#' multivariable model within each stratum. If \code{adjust_for} is supplied,
#' one adjusted model is fitted per exposure within each stratum.
#'
#' @param data A data frame containing the variables.
#' @param outcome Character scalar; name of the outcome variable. Quoted and
#'   bare names are accepted.
#' @param exposures Character vector of exposure variables to report. Quoted
#'   names are recommended in scripts, and bare names are also accepted.
#' @param stratifier Character scalar; name of the stratifying variable. Quoted
#'   and bare names are accepted.
#' @param adjust_for Optional character vector of adjustment variables.
#'   Quoted and bare names are accepted. This argument works the same way as in
#'   \code{multi_reg()}.
#' @param interaction Optional character scalar specifying one interaction term
#'   using standard formula syntax, e.g. \code{"bmi*sex"}
#' @param approach One of \code{"logit"}, \code{"firth"}, \code{"logbinomial"}, \code{"poisson"},
#'   \code{"linear"}, \code{"robpoisson"}, or \code{"negbin"}
#' @param format One of \code{"flextable"} (default) or \code{"gt"}.
#' @param theme Preset name (e.g. \code{"minimal"}, \code{"striped"}, \code{"clinical"},
#'   \code{"shaded"}, \code{"jama"}) or primitives
#'   \code{c("plain","zebra","lines","labels_bold","compact","header_shaded")}
#' @param show_ref Logical; if \code{TRUE} (default), display reference-category
#'   rows as \code{"Ref."}. If \code{FALSE}, hide reference rows; a message
#'   reminds users to use \code{show_ref = TRUE} when reference rows are needed.
#'
#' @details
#' If exposure variables have a \code{"label"} attribute, for example from
#' \code{labelled::var_label()}, those labels are used automatically in the
#' displayed table. Internal matching still uses the original column names.
#'
#' @return A list of class \code{c("gtregression","stratified_multi_reg", ...)} with:
#' \describe{
#'   \item{\code{table}}{A \code{flextable} (format = \code{"flextable"}) or
#'   \code{gt_tbl} (format = \code{"gt"}).}
#'   \item{\code{table_display}}{Wide data frame used to build the table.}
#'   \item{\code{per_stratum}}{Named list of per-stratum regression results.}
#'   \item{\code{models}}{Named list of fitted models by stratum.}
#'   \item{\code{model_summaries}}{Named list of model summaries by stratum.}
#'   \item{\code{variable_labels}}{Named character vector of display labels used
#'   for exposure variables.}
#'   \item{\code{reg_check}}{Named list of diagnostics by stratum.}
#'   \item{\code{by}, \code{levels}, \code{approach}, \code{format}, \code{source}}{Metadata fields.}
#' }
#'
#' @examples
#' birthwt_data <- data_birthwt |>
#'   transform(
#'     race = factor(race, levels = c(1, 2, 3),
#'                   labels = c("White", "Black", "Other")),
#'     smoke = factor(smoke, levels = c(0, 1), labels = c("No", "Yes")),
#'     ht = factor(ht, levels = c(0, 1), labels = c("No", "Yes")),
#'     ui = factor(ui, levels = c(0, 1), labels = c("No", "Yes")),
#'     low = factor(low, levels = c(0, 1),
#'                  labels = c("Normal BW", "Low BW"))
#'   )
#'
#' stratified_multi <- stratified_multi_reg(
#'   data = birthwt_data,
#'   outcome = "low",
#'   exposures = c("age", "lwt", "smoke", "ht"),
#'   stratifier = "race",
#'   approach = "logit",
#'   format = "gt"
#' )
#'
#' stratified_adjusted <- stratified_multi_reg(
#'   data = birthwt_data,
#'   outcome = "low",
#'   exposures = c("smoke", "ht", "ui"),
#'   stratifier = "race",
#'   adjust_for = c("age", "lwt"),
#'   approach = "logit",
#'   format = "gt"
#' )
#' @importFrom stats nobs
#' @export
stratified_multi_reg <- function(data,
                                 outcome,
                                 exposures,
                                 stratifier,
                                 adjust_for = NULL,
                                 interaction = NULL,
                                 approach = "logit",
                                 format = c("flextable", "gt"),
                                 theme = c("minimal"),
                                 show_ref = TRUE) {

  outcome <- .vars_arg(substitute(outcome), env = parent.frame())
  exposures <- .vars_arg(substitute(exposures), env = parent.frame())
  stratifier <- .vars_arg(substitute(stratifier), env = parent.frame())
  adjust_for <- .vars_arg(substitute(adjust_for), env = parent.frame(), allow_null = TRUE)
  interaction <- .interaction_arg(substitute(interaction), env = parent.frame(), allow_null = TRUE)
  approach <- .choice_arg(
    substitute(approach),
    env = parent.frame(),
    choices = c("logit","firth","logbinomial","poisson","robpoisson","linear","negbin")
  )
  approach <- .normalize_approach(approach)
  format <- .choice_arg(substitute(format), env = parent.frame(), choices = c("flextable","gt"))
  theme <- .choice_arg(substitute(theme), env = parent.frame())
  .validate_show_ref(show_ref)

  format <- match.arg(format, c("flextable","gt"))
  theme <- .resolve_theme(theme)
  variable_labels <- .var_label_map(
    data,
    unique(c(exposures, adjust_for, .interaction_vars(interaction)))
  )

  if (!stratifier %in% names(data)) {
    stop("Stratifier not found in dataset.", call. = FALSE)
  }

  data <- data[!is.na(data[[stratifier]]), , drop = FALSE]
  levs <- .strata_levels(data, stratifier)

  message("Running stratified multivariable regression by: ", stratifier)

  per_stratum <- list()
  tds <- list()
  models <- list()
  sums <- list()
  diags <- list()
  n_by_stratum <- list()

  for (lv in levs) {
    message("  > Stratum: ", stratifier, " = ", lv)

    dlev <- data[data[[stratifier]] == lv, , drop = FALSE]
    key <- as.character(lv)

    res_i <- tryCatch(
      .run_multi_core(
        data = dlev,
        outcome = outcome,
        exposures = exposures,
        approach = approach,
        adjust_for = adjust_for,
        interaction = interaction
      ),
      error = function(e) {
        warning("Skipping stratum ", lv, ": ", e$message, call. = FALSE)
        NULL
      }
    )

    if (is.null(res_i)) {
      next
    }

    res_i$table_body <- .format_interaction_levels(
      res_i$table_body, dlev, variable_labels
    )

    tds[[key]] <- res_i$table_body
    models[[key]] <- res_i$models
    sums[[key]] <- res_i$model_summaries
    diags[[key]] <- res_i$reg_check
    n_by_stratum[[key]] <- res_i$n_used

    per_stratum[[key]] <- list(
      table_body = res_i$table_body,
      models = res_i$models,
      model_summaries = res_i$model_summaries,
      reg_check = res_i$reg_check,
      n_used = res_i$n_used,
      adjusted_mode = res_i$adjusted_mode
    )
  }

  if (!length(tds)) {
    stop("No valid models across strata.", call. = FALSE)
  }

  built <- .strata_build_wide_multi(
    data,
    exposures,
    stratifier,
    tds,
    variable_labels = variable_labels,
    show_ref = show_ref
  )
  wide <- built$wide
  spanners <- built$spanners
  eff_lab <- paste("Adjusted", .get_effect_label(approach))

  footnotes <- c(
    .stratified_by_note(stratifier),
    .abbrev_note(approach),
    if (
      isTRUE(show_ref) &&
        any(unlist(lapply(tds, function(x) x$ref %in% TRUE), use.names = FALSE))
    ) .ref_note() else NULL,
    if (!is.null(adjust_for) && length(adjust_for) > 0) .adjustment_note(adjust_for) else NULL,
    if (!is.null(interaction)) .interaction_note(interaction) else NULL,
    .n_note_multi_strata(stratifier, n_by_stratum)
  )

  .message_hidden_ref_rows(
    "stratified_multi_reg",
    do.call(rbind, tds),
    show_ref
  )

  tbl <- if (format == "gt") {
    .build_gt_strata_wide_multi(wide, spanners, eff_lab, theme, footnotes)
  } else {
    .build_flex_strata_wide_multi(wide, spanners, eff_lab, theme, footnotes)
  }

  fmt_class <- if (format == "gt") "gt_strata_multi" else "ft_strata_multi"

  out <- list(
    table = tbl,
    table_display = wide,
    per_stratum = per_stratum,
    models = models,
    model_summaries = sums,
    variable_labels = variable_labels,
    reg_check = .as_reg_check_result(diags, format = format),
    by = stratifier,
    levels = levs,
    approach = approach,
    format = format,
    source = "stratified_multi_reg",
    show_ref = isTRUE(show_ref)
  )

  class(out) <- c("gtregression", "stratified_multi_reg", fmt_class, class(out))
  out
}
