#' @title Configure pakret's settings
#' @description This function lets you configure pakret's settings, for example,
#'   to customize citation templates or specify which `.bib` file should store
#'   the references.
#' @param ... Key-value pairs, separated by commas, of parameters to set. See
#'   details.
#' @details
#' Valid parameters are:
#' `r make_pkrt_set_details()`
#'
#' New settings only apply to citations that come after `pkrt_set()`. This means
#' you can redefine the same settings multiple times in the same document to
#' alter pakret's behavior for a few specific citations only.
#'
#' Use `NULL` to reset a parameter to its default value.
#' @returns This function is called for its side-effect. It returns no value.
#' @examples
#' pkrt_set(pkg = "*:pkg* :ver :ref")
#' pkrt("pakret")
#'
#' # `NULL` resets parameters to their default value
#' pkrt_set(pkg = NULL)
#' pkrt("pakret")
#' @export
pkrt_set <- function(...) {
  items <- list(...)
  check_named(items, arg = "...")
  check_settings(items)
  for (key in names(items)) {
    update_setting(key, items[[key]])
  }
}

update_setting <- function(key, value) {
  check_atomic(value, arg = key)
  check_scalar(value, arg = key)
  if (is.null(value)) {
    return(reset(key))
  }
  setting <- as_setting(value, key)
  set_option(setting)
}

reset <- function(x) {
  update(.__settings__[x])
}

update <- function(x) {
  if (is_updating_bib(x)) {
    bib_write()
    on.exit(bib_set(), add = TRUE)
  }
  do.call(set, as.list(x))
}

as_setting <- function(x, name) {
  structure(x, names = name, class = class(.__settings__[[name]]))
}

set_option <- function(x) {
  UseMethod("set_option")
}

set_option.template <- function(x) {
  check_template(x, arg = names(x))
  update(x)
}

set_option.str <- function(x) {
  check_string(x, arg = names(x))
  update(x)
}

set_option.bib <- function(x) {
  if (!is_rendering()) {
    return(invisible())
  }
  check_option_bib(x, arg = names(x))
  if (is.character(x)) {
    x[] <- bib_name(x)
  }
  check_bib_target(x)
  update(x)
}

# doc ----

.layout <- "
  * **%s**<br>
    `<%s> = %s`<br>
    %s
"

make_pkrt_set_details <- function() {
  out <- lapply(names(.details), function(key, value = .details[[key]]) {
    default <- unclass(.__settings__[[key]])
    if (!is.list(value)) {
      value <- as.list(value)
    }
    if (is.null(value$type)) {
      value$type <- typeof(default)
    }
    sprintf(dedent(.layout), key, value$type, deparse(default), value[[1]])
  })
  collapse(out)
}

.details <- list(
  bib = list(
    "Name or index of the `.bib` file to save references to.",
    type = "character|numeric"
  ),
  pkg = "Template used to cite a package.",
  pkg_list = "Template used in `pkrt_list()`.",
  r = "Template used to cite R.",
  sep = "Separator used between packages in inline chunks.",
  sep_last = "Separator used between the last two packages in inline chunks."
)
