#' Precision Thinning of Spatial Points
#'
#' This function performs thinning of spatial points by rounding their coordinates to a specified precision and removing duplicates.
#' It can perform multiple trials of this process and return the results for all or just the best trial.
#'
#' @param coordinates A numeric matrix or data frame with two columns representing the longitude and latitude of points.
#' @param precision A positive integer specifying the number of decimal places to which coordinates should be rounded. Default is 4.
#' @param trials A positive integer specifying the number of thinning trials to perform. Default is 10.
#' @param all_trials A logical value indicating whether to return results for all trials (`TRUE`) or just the first/best trial (`FALSE`). Default is `FALSE`.
#' @param priority A numeric vector of the same length as the number of points, specifying a priority weight for each point. Higher values indicate higher importance and are favored when selecting which points to retain. Priority is used to guide selection when multiple candidate points are otherwise equally valid (e.g., points in the same grid cell, with the same rounded coordinates, or with the same number of neighbors).
#' @return If `all_trials` is `FALSE`, returns a logical vector indicating which points were kept in the first trial.
#' If `all_trials` is `TRUE`, returns a list of logical vectors, one for each trial.
#'
#' @details
#' The function performs multiple trials to account for randomness in the order of point selection. By default, it returns the first trial,
#' but setting `all_trials = TRUE` will return the results of all trials.
#'
#' @examples
#' # Example usage
#' coords <- matrix(c(-123.3656, 48.4284, -123.3657, 48.4285, -123.3658, 48.4286), ncol = 2)
#' result <- precision_thinning(coords, precision = 3, trials = 5, all_trials = TRUE)
#' print(result)
#'
#' # Example with a single trial and lower precision
#' result_single <- precision_thinning(coords, precision = 2, trials = 1, all_trials = FALSE)
#' print(result_single)
#'
#' @importFrom data.table := .N
#' @export
precision_thinning <- function(coordinates, precision = 4, trials = 10, all_trials = FALSE, priority = NULL) {
  # Validate inputs
  if (!is.numeric(precision) || length(precision) != 1 || precision < 0) {
    stop("`precision` must be a non-negative integer.")
  }

  if (!is.null(priority)){
    if (!is.numeric(priority) | length(priority) != nrow(coordinates)){
      stop("'priority' must be a numeric vector with same length as number of points.")
    }

    if (any(is.na(priority))){
      warning("NA values found in 'priority'. Replacing with lowest priority (-Inf).")
      priority[is.na(priority)] <- -Inf
    }
  }

  # Initialize results list for trials
  keep_points <- vector("list", ifelse(all_trials, trials, 1))

  # Main thinning loop for the specified number of trials
  for (i in seq_len(length(keep_points))) {
    # Create a data.table with rounded coordinates and a random order or by priority
    if (is.null(priority)){
      sort_order <- stats::runif(nrow(coordinates))
    } else {
      sort_order <- rank(-priority, ties.method = "random")
    }
    keep_points_trial <- data.table::data.table(
      id = seq_len(nrow(coordinates)),
      lon = round(coordinates[, 1], precision),
      lat = round(coordinates[, 2], precision),
      rand_order = sort_order
    )

    # Randomize the order to avoid bias
    keep_points_trial <- keep_points_trial[order(keep_points_trial[["rand_order"]], decreasing = TRUE), ]

    # Mark unique points based on rounded coordinates
    keep_points_trial[, "keep"] <- !duplicated(keep_points_trial[, c("lon", "lat")])

    # Sort back to the original order and store the "keep" vector
    keep_points[[i]] <- keep_points_trial[order(keep_points_trial[["id"]]), ][["keep"]]
}

  return(keep_points)
}
