#' Prepare multi-legend specifications
#'
#' @description Sets up multiple legend specifications for multi-legends
#'   (e.g., color grouping + bubble size). Creates `lgby` and `lgbub` objects
#'   that will be passed to draw_multi_legend().
#'
#' @param settings Settings environment from tinyplot
#'
#' @returns NULL (modifies settings environment in-place)
#'
#' @keywords internal
prepare_legend_multi = function(settings) {
  env2env(
    settings,
    environment(),
    c(
      "legend",
      "legend_args",
      "by_dep",
      "lgnd_labs",
      "type",
      "type_hints",
      "pch",
      "lty",
      "lwd",
      "col",
      "bg",
      "by_continuous",
      "lgnd_cex",
      "cex_dep",
      "bubble_cex",
      "cex_fct_adj",
      "bubble_alpha",
      "bubble_bg_alpha",
      "has_sub"
    )
  )

  # Legend for grouping variable (by)
  lgby = list(
    legend_args = modifyList(
      legend_args,
      list(x.intersp = 1, y.intersp = 1),
      keep.null = TRUE
    ),
    by_dep = by_dep,
    lgnd_labs = lgnd_labs,
    type = type,
    type_hints = type_hints,
    pch = pch,
    lty = lty,
    lwd = lwd,
    col = col,
    bg = bg,
    gradient = by_continuous,
    cex = lgnd_cex,
    has_sub = has_sub
  )

  # Legend for bubble sizes
  lgbub = list(
    legend_args = modifyList(
      legend_args,
      list(title = cex_dep, ncol = 1),
      keep.null = TRUE
    ),
    lgnd_labs = names(bubble_cex),
    type = type,
    type_hints = type_hints,
    pch = pch,
    lty = lty,
    lwd = lwd,
    col = adjustcolor(par("col"), alpha.f = bubble_alpha),
    bg = adjustcolor(par("col"), alpha.f = bubble_bg_alpha),
    cex = bubble_cex * cex_fct_adj,
    has_sub = has_sub,
    draw = FALSE
  )

  env2env(environment(), settings, c("legend_args", "lgby", "lgbub"))
}


#
## Multi-Legend Rendering -----
#

#' Draw multiple legends with automatic positioning
#'
#' @description Handles multiple legends (e.g., color grouping + bubble size) by:
#'   1. Extracting dimensions from fake legends
#'   2. Calculating sub-positioning based on dimensions
#'   3. Drawing legends in ascending order of width (widest last)
#'
#' @md
#' @param legend_list A list of legend arguments, where each element is itself a
#'   list of arguments that can be passed on to [draw_legend]. Legends will be
#'   drawn vertically (top to bottom) in the order that they are provided. Note
#'   that we currently only support 2 legends, i.e. the top-level list has a
#'   maximum length of 2.
#' @param position String indicating the base keyword position for the
#'   multi-legend. Currently only `"right!"` and `"left!"` are supported.
#'
#' @returns No return value, called for side effect of drawing multiple legends.
#'
#' @seealso [draw_legend]
#'
#' @keywords internal
#'
#' @examples
#' \dontrun{
#' oldmar = par("mar")
#'
#' # Multi-legend example (color + bubble)
#'
#' l1 = list(
#'   lgnd_labs = c("Red", "Blue", "Green"),
#'   legend_args = list(title = "Colors"),
#'   pch = 16,
#'   col = c("red", "blue", "green"),
#'   type = "p"
#' )
#'
#' l2 = list(
#'   lgnd_labs = c("Tiny", "Small", "Medium", "Large", "Huge"),
#'   legend_args = list(title = "Size"),
#'   pch = 16,
#'   col = "black",
#'   cex = seq(0.5, 2.5, length.out = 5),
#'   type = "p"
#' )
#'
#' # Draw together
#' draw_multi_legend(list(l1, l2), position = "right!")
#'
#' par(mar = oldmar)
#' }
#'
#' @keywords internal
draw_multi_legend = function(
    legend_list,
    position = "right!"
) {

  # Validate inputs
  if (!is.list(legend_list) || length(legend_list) != 2) {
    stop("Currently only 2 legends are supported in multi-legend mode")
  }

  # Currently only support right!/left! positioning
  if (!grepl("right!$|left!$", position)) {
    warning(
      '\nMulti-legends currently only work with "right!" or "left!" keyword positioning.\n',
      'Reverting to "right!" default\n'
    )
    position = "right!"
  }

  # Determine sub-positions based on main position
  if (grepl("right!$", position)) {
    sub_positions = c("bottomright!", "topright!")
  } else if (grepl("left!$", position)) {
    sub_positions = c("bottomleft!", "topleft!")
  }

  # Assign positions of individual legends. Re-sanitize so the new per-legend
  # position string ("bottomright!" etc.) populates legend_args[["x"]], which
  # downstream code (e.g. build_legend_args) reads without further normalization.
  for (ll in seq_along(legend_list)) {
    legend_list[[ll]][["legend"]] = sub_positions[ll]
    legend_list[[ll]][["legend_args"]][["x"]] = NULL
    legend_list[[ll]][["legend_args"]] = sanitize_legend(
      sub_positions[ll], legend_list[[ll]][["legend_args"]]
    )
  }

  #
  ## Step 1: Extract legend dimensions (by drawing fake legends)
  #

  legend_dims = vector("list", length(legend_list))
  for (ll in seq_along(legend_list)) {
    legend_ll = legend_list[[ll]]
    legend_ll$new_plot = ll == 1  # Only draw new plot for first legend
    legend_ll$draw = FALSE
    legend_dims[[ll]] = do.call(draw_legend, legend_ll)
  }

  #
  ## Step 2: Calculate sub-positioning based on dimensions
  #

  # Extract dimensions
  lwidths = sapply(legend_dims, function(x) x$rect$w)
  lheights = sapply(legend_dims, function(x) x$rect$h)
  # For inset adjustment, default to 0.5 unless one or more of the two legends
  # is bigger than half the plot height.
  linset = if (any(lheights > 0.5)) lheights[2] / sum(lheights) else 0.5

  # Shared soma from widest legend so all legends use the same outer margin
  max_w = max(lwidths)
  lmar_vals = legend_list[[1]]$lmar %||% tpar("lmar")
  soma_target = (grconvertX(max_w, to = "lines") - grconvertX(0, to = "lines")) +
    sum(lmar_vals)

  # Determine ljust mode (shared across dual legends)
  ljust_mode = legend_list[[1]]$legend_args[["ljust"]] %||% tpar("ljust") %||% "left"
  ljust_mode = match.arg(ljust_mode, c("left", "center", "l", "c"))
  if (ljust_mode == "l") ljust_mode = "left"
  if (ljust_mode == "c") ljust_mode = "center"

  #
  ## Step 3: Reposition (via adjusted inset arg) and draw legends
  #

  # Note: we draw the legends in ascending order of width (i.e., widest legend
  #   last) in order to correctly set the overall plot dimensions.
  ## FIXME: current logic only works for "right!"/"left!" legend
  width_order = order(lwidths)

  # Quick idx for original order (needed for vertical legend placement)
  for (i in seq_along(legend_list)) legend_list[[i]]$idx = i

  for (o in seq_along(width_order)) {
    io = width_order[o]
    legend_o = legend_list[[io]]
    legend_o$new_plot = FALSE
    legend_o$draw = TRUE
    legend_o$soma_target = if (ljust_mode == "center") NULL else soma_target
    legend_o$legend_args$inset = c(0, 0)
    legend_o$legend_args$inset[1] = if (ljust_mode == "center") {
      if (o == 1) -abs(diff(lwidths)) / 2 else 0
    } else {
      0
    }
    legend_o$legend_args$inset[2] = if (legend_o$idx == 1) linset + 0.01 else 1 - linset + 0.01
    legend_o$idx = NULL
    do.call(draw_legend, legend_o)
  }

  invisible(NULL)
}
