# ==============================================================================
# svySE: exportacion de resultados
# svySE: result export
# Archivo / File: R/exportar.R
# ==============================================================================


#' Export svySE results to Excel
#'
#' Exporta los resultados generados por `svySE_calc()` a archivos Excel.
#' Exports results generated by `svySE_calc()` to Excel files.
#'
#' @param x Objeto generado por `svySE_calc()`.
#' @param file_err Archivo Excel para errores muestrales.
#' @param file_tab Archivo Excel para tabla simple.
#' @param cols_err Columnas de error a exportar.
#' @param cols_tab Columnas de tabla simple a exportar.
#' @param start_row Fila inicial de escritura.
#' @param overwrite Sobrescribir archivos existentes.
#' @param keep_na Mantener NA en Excel.
#'
#' @return Invisiblemente `TRUE`.
#'
#' @export
svySE_xlsx <- function(
    x,
    file_err = "svySE_errors.xlsx",
    file_tab = "svySE_simple.xlsx",
    cols_err = svySE_cols_err("full"),
    cols_tab = svySE_cols_tab("full"),
    start_row = 10,
    overwrite = TRUE,
    keep_na = FALSE
) {
  
  # ---------------------------------------------------------------------------
  # Validaciones generales
  # General validations
  # ---------------------------------------------------------------------------
  
  if (!inherits(x, "svySE_result")) {
    stop(
      "`x` debe ser un objeto generado por `svySE_calc()` / must come from `svySE_calc()`.",
      call. = FALSE
    )
  }
  
  if (!requireNamespace("openxlsx", quietly = TRUE)) {
    stop(
      "El paquete `openxlsx` es requerido / package `openxlsx` is required.",
      call. = FALSE
    )
  }
  
  if (!is.null(file_err)) {
    svySE_chk_cols(cols_err, svySE_cols_err_all(), "cols_err")
  }
  
  if (!is.null(file_tab)) {
    svySE_chk_cols(cols_tab, svySE_cols_tab_all(), "cols_tab")
  }
  
  if (!is.numeric(start_row) || length(start_row) != 1 || start_row < 4) {
    stop(
      "`start_row` debe ser numerico y mayor o igual a 4 / must be numeric and >= 4.",
      call. = FALSE
    )
  }
  
  svySE_chk_bool(overwrite, "overwrite")
  svySE_chk_bool(keep_na, "keep_na")
  
  # ---------------------------------------------------------------------------
  # Exportar libro de errores muestrales
  # Export sampling error workbook
  # ---------------------------------------------------------------------------
  
  if (!is.null(file_err)) {
    
    wb_err <- openxlsx::createWorkbook()
    
    for (ind in x$meta$indicators) {
      
      sheet <- svySE_sheet_name(paste0(ind, "_Error"))
      
      openxlsx::addWorksheet(wb_err, sheet)
      
      svySE_write_err_sheet(
        wb = wb_err,
        sheet = sheet,
        result_ind = x$results[[ind]]$error,
        group_vars = x$meta$group_vars,
        group_labels = x$meta$group_labels,
        cols = cols_err,
        start_row = start_row,
        keep_na = keep_na
      )
    }
    
    openxlsx::saveWorkbook(
      wb = wb_err,
      file = file_err,
      overwrite = overwrite
    )
  }
  
  # ---------------------------------------------------------------------------
  # Exportar libro de tabla simple
  # Export simple table workbook
  # ---------------------------------------------------------------------------
  
  if (!is.null(file_tab)) {
    
    wb_tab <- openxlsx::createWorkbook()
    
    for (ind in x$meta$indicators) {
      
      sheet <- svySE_sheet_name(paste0(ind, "_Simple"))
      
      openxlsx::addWorksheet(wb_tab, sheet)
      
      svySE_write_tab_sheet(
        wb = wb_tab,
        sheet = sheet,
        result_ind = x$results[[ind]]$simple,
        group_vars = x$meta$group_vars,
        group_labels = x$meta$group_labels,
        cols = cols_tab,
        start_row = start_row,
        keep_na = keep_na
      )
    }
    
    openxlsx::saveWorkbook(
      wb = wb_tab,
      file = file_tab,
      overwrite = overwrite
    )
  }
  
  invisible(TRUE)
}


# ==============================================================================
# Escritura de hoja de errores
# Error sheet writing
# ==============================================================================

#' @keywords internal
svySE_write_err_sheet <- function(
    wb,
    sheet,
    result_ind,
    group_vars,
    group_labels,
    cols,
    start_row,
    keep_na = FALSE
) {
  
  col_now <- 1
  divisions <- names(result_ind)
  
  for (i in seq_along(divisions)) {
    
    div <- divisions[i]
    is_total <- i == 1
    
    tab <- result_ind[[div]]
    
    if (is_total) {
      tab_out <- tab[, c(group_vars, cols), drop = FALSE]
    } else {
      tab_out <- tab[, cols, drop = FALSE]
    }
    
    svySE_head_err(
      wb = wb,
      sheet = sheet,
      col_start = col_now,
      row_data = start_row,
      group_labels = group_labels,
      only_values = !is_total,
      division_name = div,
      cols = cols
    )
    
    openxlsx::writeData(
      wb = wb,
      sheet = sheet,
      x = tab_out,
      startRow = start_row,
      startCol = col_now,
      colNames = FALSE,
      keepNA = keep_na
    )
    
    width <- if (is_total) {
      length(group_vars) + length(cols)
    } else {
      length(cols)
    }
    
    col_now <- col_now + width
  }
  
  svySE_style_sheet(
    wb = wb,
    sheet = sheet,
    max_col = col_now - 1,
    start_row = start_row
  )
  
  invisible(TRUE)
}


# ==============================================================================
# Escritura de hoja simple
# Simple sheet writing
# ==============================================================================

#' @keywords internal
svySE_write_tab_sheet <- function(
    wb,
    sheet,
    result_ind,
    group_vars,
    group_labels,
    cols,
    start_row,
    keep_na = FALSE
) {
  
  col_now <- 1
  divisions <- names(result_ind)
  
  for (i in seq_along(divisions)) {
    
    div <- divisions[i]
    is_total <- i == 1
    
    tab <- result_ind[[div]]
    
    if (is_total) {
      tab_out <- tab[, c(group_vars, cols), drop = FALSE]
    } else {
      tab_out <- tab[, cols, drop = FALSE]
    }
    
    svySE_head_tab(
      wb = wb,
      sheet = sheet,
      col_start = col_now,
      row_data = start_row,
      group_labels = group_labels,
      only_values = !is_total,
      division_name = div,
      cols = cols
    )
    
    openxlsx::writeData(
      wb = wb,
      sheet = sheet,
      x = tab_out,
      startRow = start_row,
      startCol = col_now,
      colNames = FALSE,
      keepNA = keep_na
    )
    
    width <- if (is_total) {
      length(group_vars) + length(cols)
    } else {
      length(cols)
    }
    
    col_now <- col_now + width
  }
  
  svySE_style_sheet(
    wb = wb,
    sheet = sheet,
    max_col = col_now - 1,
    start_row = start_row
  )
  
  invisible(TRUE)
}


# ==============================================================================
# Encabezados de tabla de errores
# Error table headers
# ==============================================================================

#' @keywords internal
svySE_head_err <- function(
    wb,
    sheet,
    col_start,
    row_data,
    group_labels,
    only_values = FALSE,
    division_name = NULL,
    cols
) {
  
  row_div <- row_data - 3
  row_top <- row_data - 2
  row_low <- row_data - 1
  
  col_met <- if (only_values) {
    col_start
  } else {
    col_start + length(group_labels)
  }
  
  if (!only_values) {
    for (j in seq_along(group_labels)) {
      openxlsx::writeData(
        wb,
        sheet,
        x = data.frame(V1 = group_labels[j]),
        startRow = row_div,
        startCol = col_start + j - 1,
        colNames = FALSE
      )
    }
  }
  
  if (!is.null(division_name)) {
    openxlsx::writeData(
      wb,
      sheet,
      x = data.frame(V1 = division_name),
      startRow = row_div,
      startCol = col_met,
      colNames = FALSE
    )
  }
  
  labels <- svySE_lbl_err(cols)
  sub_labels <- svySE_sublbl_err(cols)
  
  for (j in seq_along(cols)) {
    
    openxlsx::writeData(
      wb,
      sheet,
      x = data.frame(V1 = labels[j]),
      startRow = row_top,
      startCol = col_met + j - 1,
      colNames = FALSE
    )
    
    if (!is.na(sub_labels[j])) {
      openxlsx::writeData(
        wb,
        sheet,
        x = data.frame(V1 = sub_labels[j]),
        startRow = row_low,
        startCol = col_met + j - 1,
        colNames = FALSE
      )
    }
  }
  
  invisible(TRUE)
}


# ==============================================================================
# Encabezados de tabla simple
# Simple table headers
# ==============================================================================

#' @keywords internal
svySE_head_tab <- function(
    wb,
    sheet,
    col_start,
    row_data,
    group_labels,
    only_values = FALSE,
    division_name = NULL,
    cols
) {
  
  row_div <- row_data - 3
  row_top <- row_data - 2
  row_low <- row_data - 1
  
  col_met <- if (only_values) {
    col_start
  } else {
    col_start + length(group_labels)
  }
  
  if (!only_values) {
    for (j in seq_along(group_labels)) {
      openxlsx::writeData(
        wb,
        sheet,
        x = data.frame(V1 = group_labels[j]),
        startRow = row_div,
        startCol = col_start + j - 1,
        colNames = FALSE
      )
    }
  }
  
  if (!is.null(division_name)) {
    openxlsx::writeData(
      wb,
      sheet,
      x = data.frame(V1 = division_name),
      startRow = row_div,
      startCol = col_met,
      colNames = FALSE
    )
  }
  
  labels <- svySE_lbl_tab(cols)
  
  for (j in seq_along(cols)) {
    
    openxlsx::writeData(
      wb,
      sheet,
      x = data.frame(V1 = labels$top[j]),
      startRow = row_top,
      startCol = col_met + j - 1,
      colNames = FALSE
    )
    
    openxlsx::writeData(
      wb,
      sheet,
      x = data.frame(V1 = labels$low[j]),
      startRow = row_low,
      startCol = col_met + j - 1,
      colNames = FALSE
    )
  }
  
  invisible(TRUE)
}


# ==============================================================================
# Etiquetas de columnas
# Column labels
# ==============================================================================

#' @keywords internal
svySE_lbl_err <- function(cols) {
  
  dict <- c(
    est_abs = "Estimacion",
    est_pct = "Estimacion",
    se_abs = "Error estandar",
    se_pct = "Error estandar",
    ci_l_abs = "95% de intervalo de confianza",
    ci_l_pct = "95% de intervalo de confianza",
    ci_u_abs = "95% de intervalo de confianza",
    ci_u_pct = "95% de intervalo de confianza",
    cv = "Coeficiente de variacion",
    deff = "Efecto de diseno",
    n_unw = "Recuento no ponderado"
  )
  
  unname(dict[cols])
}


#' @keywords internal
svySE_sublbl_err <- function(cols) {
  
  dict <- c(
    est_abs = "Abs.",
    est_pct = "%",
    se_abs = "Abs.",
    se_pct = "%",
    ci_l_abs = "Inf. Abs.",
    ci_l_pct = "Inf. %",
    ci_u_abs = "Sup. Abs.",
    ci_u_pct = "Sup. %",
    cv = NA,
    deff = NA,
    n_unw = NA
  )
  
  unname(dict[cols])
}


#' @keywords internal
svySE_lbl_tab <- function(cols) {
  
  top <- c(
    freq_0 = "0",
    pct_0 = "0",
    freq_1 = "1",
    pct_1 = "1",
    freq_total = "TOTAL",
    pct_total = "TOTAL"
  )
  
  low <- c(
    freq_0 = "Abs.",
    pct_0 = "%",
    freq_1 = "Abs.",
    pct_1 = "%",
    freq_total = "Abs.",
    pct_total = "%"
  )
  
  data.frame(
    top = unname(top[cols]),
    low = unname(low[cols]),
    stringsAsFactors = FALSE
  )
}


# ==============================================================================
# Estilos de Excel
# Excel styles
# ==============================================================================

#' @keywords internal
svySE_style_sheet <- function(
    wb,
    sheet,
    max_col,
    start_row
) {
  
  header_style <- openxlsx::createStyle(
    textDecoration = "bold",
    halign = "center",
    valign = "center",
    border = "TopBottomLeftRight",
    wrapText = TRUE
  )
  
  openxlsx::addStyle(
    wb = wb,
    sheet = sheet,
    style = header_style,
    rows = (start_row - 3):(start_row - 1),
    cols = 1:max_col,
    gridExpand = TRUE,
    stack = TRUE
  )
  
  openxlsx::setColWidths(
    wb = wb,
    sheet = sheet,
    cols = 1:max_col,
    widths = "auto"
  )
  
  openxlsx::freezePane(
    wb = wb,
    sheet = sheet,
    firstActiveRow = start_row,
    firstActiveCol = 1
  )
  
  invisible(TRUE)
}


# ==============================================================================
# Utilidades
# Utilities
# ==============================================================================

#' @keywords internal
svySE_sheet_name <- function(x) {
  
  x <- gsub("[\\[\\]\\:\\*\\?\\/\\\\]", "_", x)
  
  if (nchar(x) > 31) {
    x <- substr(x, 1, 31)
  }
  
  x
}
