# ============================================================
# KRONX :: data_io.R
# CSV ingestion and log-return construction.
# ============================================================

#' Read a close-price series from a CSV file.
#'
#' The function is case-insensitive: it accepts \code{close}, \code{Close},
#' \code{CLOSE}, or any capitalisation.  The first matching column is used.
#'
#' @param file   Path to the CSV file.
#' @param close_col  Name of the close-price column (default \code{"close"}).
#'
#' @return A numeric vector of finite close prices (length >= 10).
#' @export

read_close_series <- function(file, close_col = "close") {
  if (!file.exists(file)) {
    stop(sprintf("Input file '%s' not found.", file), call. = FALSE)
  }

  dat       <- utils::read.csv(file, check.names = FALSE)
  col_lower <- tolower(names(dat))
  target    <- tolower(close_col)

  if (!(target %in% col_lower)) {
    hits <- grep("close", col_lower, value = FALSE, fixed = TRUE)
    if (length(hits) == 0L) {
      stop(sprintf(
        "Column '%s' not found. Available columns: %s",
        close_col, paste(names(dat), collapse = ", ")
      ), call. = FALSE)
    }
    target <- col_lower[hits[1L]]
    message(sprintf(
      "close_col '%s' not found; using '%s' instead.",
      close_col, names(dat)[hits[1L]]
    ))
  }

  raw_px <- dat[[which(col_lower == target)[1L]]]
  px <- suppressWarnings(as.numeric(raw_px))

  if (any(is.na(px) & !is.na(raw_px))) {
    stop("Close-price column must be numeric.", call. = FALSE)
  }

  px <- px[is.finite(px)]

  if (length(px) < 10L) {
    stop("Not enough finite close observations (need >= 10).", call. = FALSE)
  }

  px
}

#' Compute log returns from a close-price vector.
#'
#' @param close Numeric vector of close prices.
#' @return Numeric vector of log returns (length = \code{length(close) - 1}).
#' @export
compute_log_returns <- function(close) {
  # ---- Type check ----
  if (!is.numeric(close)) {
    stop("'close' must be a numeric vector.", call. = FALSE)
  }
  close <- as.numeric(close)
  # ---- Length check ----
  if (length(close) < 2L) {
    stop("Need at least 2 observations to compute returns.", call. = FALSE)
  }
  # ---- Finite check ----
  if (any(!is.finite(close))) {
    stop("Input contains non-finite values.", call. = FALSE)
  }
  # ---- Positivity check (CRITICAL for log) ----
  if (any(close <= 0)) {
    stop("Prices must be strictly positive.", call. = FALSE)
  }
  # ---- Compute returns ----
  ret <- diff(log(close))
  # ---- Post-check ----
  if (any(!is.finite(ret))) {
    stop("Computed returns contain non-finite values.", call. = FALSE)
  }
  ret
}
