# post-render: mirror Quarto book output with robocopy (/MIR), preserving upload state.
#
# Project env: copy .Renviron.example to .Renviron (gitignored) and edit.
# Quarto post-render may not load `.Renviron` automatically; this script reads it explicitly.

renv_proj <- here::here(".Renviron")
if (file.exists(renv_proj)) {
  readRenviron(renv_proj)
}

# Optional environment variables (see .Renviron.example):
#   BOOK_MIRROR_FROM          — source (default: <project>/_book)
#   BOOK_MIRROR_TO            — destination (default: <project>/_book_sync2)
#   BOOK_MIRROR_EXCLUDE_DIRS  — comma-separated robocopy /XD names (default: ".sync")

resolve_mirror_path <- function(env_name, default_relative) {
  raw <- trimws(Sys.getenv(env_name, ""))
  if (!nzchar(raw)) {
    return(here::here(default_relative))
  }
  if (grepl("^[A-Za-z]:[/\\\\]|^\\\\\\\\", raw)) {
    return(path.expand(raw))
  }
  here::here(raw)
}

mirror_paths_overlap <- function(from, to) {
  f <- normalizePath(from, winslash = "\\", mustWork = TRUE)
  t <- normalizePath(to, winslash = "\\", mustWork = TRUE)
  if (tolower(f) == tolower(t)) {
    return(TRUE)
  }
  f2 <- paste0(f, "\\")
  t2 <- paste0(t, "\\")
  startsWith(t, f2) || startsWith(f, t2)
}

parse_exclude_dirs <- function() {
  raw <- trimws(Sys.getenv("BOOK_MIRROR_EXCLUDE_DIRS", ".sync"))
  if (!nzchar(raw)) {
    return(character())
  }
  parts <- strsplit(raw, ",", fixed = TRUE)[[1L]]
  parts <- trimws(parts)
  parts[nzchar(parts)]
}

dir_from <- resolve_mirror_path("BOOK_MIRROR_FROM", "_book")
dir_to <- resolve_mirror_path("BOOK_MIRROR_TO", "_book_sync2")

if (!dir.exists(dir_from)) {
  stop(
    "BOOK_MIRROR_FROM does not exist or is not a directory:\n  ",
    dir_from,
    call. = FALSE
  )
}

if (!dir.exists(dir_to)) {
  dir.create(dir_to, recursive = TRUE)
}

norm_from <- normalizePath(dir_from, winslash = "\\", mustWork = TRUE)
norm_to <- normalizePath(dir_to, winslash = "\\", mustWork = TRUE)

if (mirror_paths_overlap(norm_from, norm_to)) {
  stop(
    "Refusing mirror: source and destination are the same path, or one is nested inside the other.\n",
    "  from: ", norm_from, "\n",
    "  to:   ", norm_to,
    call. = FALSE
  )
}

exclude_dirs <- parse_exclude_dirs()
if (!length(exclude_dirs)) {
  exclude_dirs <- ".sync"
}
xd_args <- as.vector(rbind(rep("/XD", length(exclude_dirs)), exclude_dirs))

args <- c(
  shQuote(norm_from, type = "cmd2"),
  shQuote(norm_to, type = "cmd2"),
  "/MIR",
  xd_args,
  "/NFL", "/NDL", "/NJH", "/NJS"
)

status <- system2("robocopy", args, stdout = "", stderr = "")

code <- if (is.null(status)) 0L else status
if (code > 7L) {
  stop("robocopy mirror failed, exit code: ", code, call. = FALSE)
}

message(
  "Mirrored:\n  ",
  norm_from, "\n  -> ", norm_to,
  if (length(exclude_dirs)) {
    paste0("\n  (/XD ", paste(exclude_dirs, collapse = ", "), ")")
  } else {
    ""
  }
)
