# knitr.R - Knitr and Quarto integration
#
# Functions to display cardargus cards in R Markdown and Quarto documents.

#' Display card in knitr/Quarto document (SVG via data URI)
#'
#' @description
#' Embeds the SVG as an `<img>` using a `data:image/svg+xml;base64,...` URI.
#' This is more robust than inline `<svg>` for Pandoc/pkgdown (avoids "unclosed div"
#' warnings). For non-HTML outputs, it falls back to `include_card_png()`.
#'
#' @param svg_string SVG string from `svg_card()`.
#' @param width Display width (CSS units), e.g. `"100%"`, `"500px"`.
#' @param alt Alternative text for accessibility.
#' @param dpi Fallback DPI used when output is not HTML (default 300).
#' @param engine Rendering engine for non-HTML output: `"auto"`, `"chrome"`, or `"rsvg"`.
#' @return Knitr output for the current format.
#' @export
include_card <- function(svg_string,
                         width = "100%",
                         alt = "Card generated by cardargus",
                         dpi = 300,
                         engine = c("auto", "chrome", "rsvg")) {
  if (!requireNamespace("knitr", quietly = TRUE)) {
    cli::cli_abort("Package {.pkg knitr} is required.")
  }
  if (!requireNamespace("base64enc", quietly = TRUE)) {
    cli::cli_abort(c(
      "x" = "Package {.pkg base64enc} is required.",
      "i" = "Install with: {.code install.packages('base64enc')}"
    ))
  }
  
  engine <- match.arg(engine)
  
  # Non-HTML output: SVG-in-IMG won't work reliably -> use PNG
  if (!knitr::is_html_output()) {
    return(include_card_png(svg_string, dpi = dpi, width = width, alt = alt, engine = engine))
  }
  
  # Sanitize to avoid issues with strict parsers / odd metadata
  svg <- sanitize_svg_for_raster(svg_string)
  
  # Ensure UTF-8 bytes for base64
  svg_raw <- charToRaw(enc2utf8(svg))
  svg_b64 <- base64enc::base64encode(svg_raw)
  data_uri <- paste0("data:image/svg+xml;base64,", svg_b64)
  
  html <- sprintf(
    '<div class="cardargus-card" style="width:%s; max-width:100%%;">
  <img src="%s" alt="%s" style="width:100%%; height:auto;" />
</div>',
    width, data_uri, alt
  )
  
  # Blank lines help Pandoc treat it as a raw HTML block
  knitr::asis_output(paste0("\n\n", html, "\n\n"))
}

#' Display card as PNG in knitr/Quarto document
#'
#' Converts an SVG card to PNG and displays it in R Markdown or Quarto documents.
#' Recommended for better compatibility across HTML/PDF/Word.
#'
#' @param svg_string SVG string from `svg_card()`.
#' @param dpi Rasterization DPI (default 300).
#' @param width Display width (CSS units) for HTML outputs.
#' @param alt Alternative text for accessibility.
#' @param background Background color passed to `svg_to_png()` (default transparent).
#' @param engine Rendering engine: `"auto"` (uses Chrome if available, else rsvg),
#'   `"chrome"` (headless Chrome via chromote), or `"rsvg"` (librsvg/magick).
#'   Chrome provides better font rendering for Google Fonts.
#' @return Knitr output for the current format.
#' @export
include_card_png <- function(svg_string,
                             dpi = 300,
                             width = "100%",
                             alt = "Card generated by cardargus",
                             background = "transparent",
                             engine = c("auto", "chrome", "rsvg")) {
  if (!requireNamespace("knitr", quietly = TRUE)) {
    cli::cli_abort("Package {.pkg knitr} is required.")
  }
  if (!requireNamespace("base64enc", quietly = TRUE)) {
    cli::cli_abort(c(
      "x" = "Package {.pkg base64enc} is required.",
      "i" = "Install with: {.code install.packages('base64enc')}"
    ))
  }
  
  engine <- match.arg(engine)
  
  # Always rasterize via package converter (sanitizes + DPI scaling)
  png_path <- save_card_for_knitr(
    svg_string, 
    filename = "cardargus_card", 
    format = "png", 
    dpi = dpi,
    engine = engine
  )
  
  if (knitr::is_html_output()) {
    # Embed as data URI (portable for pkgdown/GitHub)
    png_raw <- readBin(png_path, "raw", n = file.info(png_path)$size)
    png_b64 <- base64enc::base64encode(png_raw)
    data_uri <- paste0("data:image/png;base64,", png_b64)
    
    html <- sprintf(
      '<div class="cardargus-card" style="width:%s; max-width:100%%;">
  <img src="%s" alt="%s" style="width:100%%; height:auto;" />
</div>',
      width, data_uri, alt
    )
    
    return(knitr::asis_output(paste0("\n\n", html, "\n\n")))
  }
  
  # Non-HTML output: write a real file and include it
  knitr::include_graphics(png_path)
}

#' Save card and return path for knitr
#'
#' Saves a card to a file and returns the path for use in knitr chunks.
#'
#' @param svg_string SVG string from `svg_card()`.
#' @param filename Output filename (without extension).
#' @param format Output format: `"svg"` or `"png"`.
#' @param dpi Rasterization DPI for PNG (default 300).
#' @param dir Output directory (defaults to knitr figure directory or tempdir()).
#' @param engine Rendering engine for PNG: `"auto"`, `"chrome"`, or `"rsvg"`.
#' @return Path to the saved file.
#' @export
save_card_for_knitr <- function(svg_string,
                                filename = "card",
                                format = c("svg", "png"),
                                dpi = 300,
                                dir = NULL,
                                engine = c("auto", "chrome", "rsvg")) {
  format <- match.arg(format)
  engine <- match.arg(engine)
  
  if (is.null(dir)) {
    if (requireNamespace("knitr", quietly = TRUE)) {
      fig_path <- knitr::opts_chunk$get("fig.path")
      if (!is.null(fig_path) && nzchar(fig_path)) {
        dir <- dirname(fig_path)
      } else {
        dir <- tempdir()
      }
    } else {
      dir <- tempdir()
    }
  }
  
  if (!dir.exists(dir)) dir.create(dir, recursive = TRUE)
  
  output_path <- file.path(dir, paste0(filename, ".", format))
  
  if (format == "svg") {
    save_svg(svg_string, output_path)
  } else {
    # Determine which engine to use
    use_chrome <- FALSE
    
    if (engine == "chrome") {
      if (chrome_available()) {
        use_chrome <- TRUE
      } else {
        cli::cli_warn("Chrome not available, falling back to rsvg.")
      }
    } else if (engine == "auto") {
      # Auto: prefer Chrome if available (better font rendering)
      use_chrome <- chrome_available()
    }
    
    if (use_chrome) {
      svg_to_png_chrome(svg_string, output_path, dpi = dpi, background = "transparent")
    } else {
      svg_to_png(svg_string, output_path, dpi = dpi, background = "transparent")
    }
  }
  
  output_path
}

#' Register cardargus knitr engine
#'
#' Registers a custom knitr engine named `"cardargus"` so that SVG cards can be
#' rendered directly from chunks.
#'
#' @return Invisible NULL.
#' @export
#' @examples
#' # In your setup chunk:
#' register_cardargus_knitr()
#' 
#' # Then use cardargus as chunk engine:
#' # ```{cardargus}
#' # svg_card(title = "My Card", ...)
#' # ```
register_cardargus_knitr <- function() {
  if (!requireNamespace("knitr", quietly = TRUE)) {
    cli::cli_abort(c(
      "x" = "Package {.pkg knitr} is required.",
      "i" = "Install with: {.code install.packages('knitr')}"
    ))
  }
  
  knitr::knit_engines$set(cardargus = function(options) {
    code <- paste(options$code, collapse = "\n")
    svg_result <- eval(parse(text = code), envir = knitr::knit_global())
    
    if (is.character(svg_result) && grepl("^<\\?xml|^<svg", svg_result)) {
      out_width <- if (!is.null(options$out.width)) options$out.width else "100%"
      knitr::knit_print(include_card_png(svg_result, width = out_width))
    } else {
      knitr::engine_output(options, code, svg_result)
    }
  })
  
  invisible(NULL)
}

#' Create a grob for grid/ggplot2
#'
#' Wrap an SVG card as a raster grob so it can be used with grid graphics.
#'
#' @param svg_string SVG string from `svg_card()`.
#' @param dpi Rasterization DPI (default 150).
#' @param engine Rendering engine: `"auto"`, `"chrome"`, or `"rsvg"`.
#' @return A `grid::rasterGrob` object.
#' @export
card_to_grob <- function(svg_string, dpi = 150, engine = c("auto", "chrome", "rsvg")) {
  if (!requireNamespace("grid", quietly = TRUE)) {
    cli::cli_abort("Package {.pkg grid} is required.")
  }
  if (!requireNamespace("magick", quietly = TRUE)) {
    cli::cli_abort(c(
      "x" = "Package {.pkg magick} is required.",
      "i" = "Install with: {.code install.packages('magick')}"
    ))
  }
  
  engine <- match.arg(engine)
  
  # Determine which engine to use
  use_chrome <- FALSE
  if (engine == "chrome") {
    if (chrome_available()) {
      use_chrome <- TRUE
    } else {
      cli::cli_warn("Chrome not available, falling back to rsvg.")
    }
  } else if (engine == "auto") {
    use_chrome <- chrome_available()
  }
  
  # Rasterize
  tmp_png <- tempfile(fileext = ".png")
  if (use_chrome) {
    svg_to_png_chrome(svg_string, tmp_png, dpi = dpi, background = "transparent")
  } else {
    svg_to_png(svg_string, tmp_png, dpi = dpi, background = "transparent")
  }
  
  img <- magick::image_read(tmp_png)
  raster_data <- as.raster(img)
  
  grid::rasterGrob(raster_data, interpolate = TRUE)
}