#' @export
#' @title Fast Append context to a Word document
#' @description
#' This function is used to append content to a Word document
#' in a fast way.
#'
#' It does not use the XML tree of the document
#' neither the cursor that is responsible for increasing the
#' performance of Word document generation when looping
#' over a large number of elements.
#'
#' This function must be used with the `write_elements_to_context()`
#' and `body_append_stop_context()` functions:
#'
#' 1. `body_append_start_context()` creates a context and returns
#'    a list with the context and the file connection.
#' 2. `write_elements_to_context()` writes the elements to the context
#'   file connection.
#' 3. `body_append_stop_context()` closes the file connection and
#'   replaces the XML in the document with the new XML.
#' @param x an rdocx object
#' @param additional_ns a named character vector of additional XML namespaces
#' to be added to the root node of the document. The names of the vector
#' are the namespace prefixes and the values are the namespace URIs.
#'
#' This argument is useful when the elements to be added to the document require
#' additional namespaces that are not already present in the document and not
#' part of the xml generated by `to_wml()`. Simple users are not expected
#' to use this argument. It is mainly intended for developers of officer extensions.
#' @return `body_append_start_context()` returns a list representing the context
#' that contains:
#' - `doc`: the original document object
#' - `file_con`: the file connection to the context
#' - `file_path`: the path to the context file
#' - `final_str`: the final XML string to be appended to the document
#' later when calling `body_append_stop_context()`.
#'
#' This object should not be modified by the user but instead
#' passed to `write_elements_to_context()` and `body_append_stop_context()`.
#'
#' `write_elements_to_context()` returns the context object.
#'
#' `body_append_stop_context()` returns the `rdocx` object with the
#' cursor position set to the end of the document.
#' @importFrom utils tail
#' @rdname body_append_context
#' @example inst/examples/example_body_append_context.R
#' @family functions for adding content
body_append_start_context <- function(x, additional_ns = character()) {
  if (!is.character(additional_ns)) {
    cli::cli_abort("`additional_ns` must be a named character vector.")
  }
  if (length(additional_ns) > 0 && is.null(names(additional_ns))) {
    cli::cli_abort("`additional_ns` must be a named character vector.")
  }
  if (!inherits(x, "rdocx")) {
    cli::cli_abort("`x` must be an rdocx object.")
  }

  current_xml_file <- tempfile(fileext = ".txt")
  xml_doc <- docx_body_xml(x)
  current_xml_str <- as.character(xml_doc)
  writeLines(current_xml_str, current_xml_file, useBytes = TRUE)
  current_xml_str_lines <- readLines(
    current_xml_file,
    encoding = "UTF-8",
    warn = FALSE
  )
  unlink(current_xml_file)

  section_start <- grepl("<w:sectPr", current_xml_str_lines)
  default_section_pos <- tail(which(section_start), n = 1)

  if (length(additional_ns) > 0) {
    add_ns_str <- sprintf(
      " xmlns:%s=\"%s\"",
      names(additional_ns),
      additional_ns
    )
    add_ns_str <- paste0(add_ns_str, collapse = "")
    current_xml_str_lines[2] <- gsub(
      "<w:document",
      paste0("<w:document", add_ns_str),
      current_xml_str_lines[2],
      fixed = TRUE
    )
  }

  output_file <- tempfile(fileext = ".txt")
  con <- file(output_file, open = "w", encoding = "UTF-8")
  cat(
    current_xml_str_lines[seq_len(default_section_pos - 1)],
    sep = "\n",
    file = con,
    append = TRUE
  )

  out <- list(
    doc = x,
    file_con = con,
    file_path = output_file,
    final_str = current_xml_str_lines[
      default_section_pos:length(current_xml_str_lines)
    ]
  )
  class(out) <- "rdocx_append_context"
  out
}

#' @rdname body_append_context
#' @param ... elements to be written to the context. These can be
#' paragraphs, tables, images, etc. The elements should have an associated
#' `to_wml()` method that converts them to WML format.
#' @param context the context object created by `body_append_start_context()`.
#' @export
write_elements_to_context <- function(context, ...) {
  objects <- list(...)
  for (obj in objects) {
    cat(
      to_wml(obj, add_ns = TRUE),
      sep = "\n",
      file = context$file_con,
      append = TRUE
    )
  }
  context
}

#' @rdname body_append_context
#' @export
body_append_stop_context <- function(context) {
  cat(context$final_str, sep = "\n", file = context$file_con, append = TRUE)
  close(context$file_con)
  context$doc$doc_obj$replace_xml(context$file_path)
  unlink(context$file_path)
  x <- context$doc
  x$officer_cursor <- officer_cursor(x$doc_obj$get())
  x
}
