#' Visualize Seurat embeddings with CellWindX palettes
#'
#' @description
#' `CellWindX_DimPlot()` visualizes UMAP or t-SNE embeddings from a processed
#' Seurat object using built-in CellWindX color palettes. The function is designed
#' for annotated single-cell objects and supports three predefined aesthetic
#' styles: Chinese landscape-inspired, Chongqing modern, and girlish palettes.
#'
#' @details
#' This function is a wrapper around [Seurat::DimPlot()] with customized color
#' palettes and publication-oriented theme settings. It requires that the
#' selected dimensional reduction has already been computed and stored in
#' `object@reductions`, such as by running [Seurat::RunUMAP()] or
#' [Seurat::RunTSNE()].
#'
#' The built-in palettes support up to 20 annotated groups. If the number of
#' groups in `group.by` exceeds 20, the function will return an error.
#'
#' @param object A Seurat object containing dimensional reduction results.
#' @param group.by Character string. Metadata column used to color cells.
#'   Default is `"seurat_annotations"`.
#' @param reduction Character string. Dimensional reduction to visualize.
#'   One of `"umap"` or `"tsne"`.
#' @param palette Character string. Built-in CellWindX palette to use.
#'   One of `"shanshui"`, `"chongqing_modern"`, or `"girlish"`.
#' @param label Logical. Whether to show group labels on the plot.
#'   Default is `TRUE`.
#' @param repel Logical. Whether to repel text labels using ggrepel.
#'   Default is `TRUE`.
#' @param pt.size Numeric. Point size passed to [Seurat::DimPlot()].
#'   Default is `0.6`.
#' @param label.size Numeric. Label font size. Reserved for future extension.
#'   Default is `4`.
#' @param alpha Numeric or `NULL`. Point transparency. Values should usually
#'   range from 0 to 1. Default is `0.9`.
#' @param shuffle Logical. Whether to randomly shuffle plotting order of cells.
#'   Default is `TRUE`.
#' @param seed Integer. Random seed used when `shuffle = TRUE`.
#'   Default is `123`.
#' @param title Character string or `NULL`. Plot title. If `NULL`, a default
#'   title is automatically generated.
#' @param legend.position Character string. Legend position passed to
#'   [ggplot2::theme()]. Common values include `"right"`, `"left"`,
#'   `"bottom"`, `"top"`, and `"none"`. Default is `"right"`.
#'
#' @return A ggplot object generated from [Seurat::DimPlot()].
#'
#' @author Xiaofeng Yang, Chongqing Medical University
#'
#' @examples
#' data("pbmc_small", package = "SeuratObject")
#'
#' p1 <- CellWindX_DimPlot(
#'   object = pbmc_small,
#'   group.by = "groups",
#'   reduction = "tsne",
#'   palette = "shanshui"
#' )
#'
#' p1
#'
#' @export
CellWindX_DimPlot <- function(
    object,
    group.by = "seurat_annotations",
    reduction = c("umap", "tsne"),
    palette = c("shanshui", "chongqing_modern", "girlish"),
    label = TRUE,
    repel = TRUE,
    pt.size = 0.6,
    label.size = 4,
    alpha = 0.9,
    shuffle = TRUE,
    seed = 123,
    title = NULL,
    legend.position = "right"
) {

  # -----------------------------
  # 1. Check packages
  # -----------------------------
  if (!requireNamespace("Seurat", quietly = TRUE)) {
    stop("Package 'Seurat' is required.")
  }
  if (!requireNamespace("ggplot2", quietly = TRUE)) {
    stop("Package 'ggplot2' is required.")
  }

  # -----------------------------
  # 2. Match arguments
  # -----------------------------
  reduction <- match.arg(reduction)
  palette <- match.arg(palette)

  # -----------------------------
  # 3. Check Seurat object
  # -----------------------------
  if (!inherits(object, "Seurat")) {
    stop("'object' must be a Seurat object.")
  }

  if (!group.by %in% colnames(object@meta.data)) {
    stop(
      paste0(
        "The metadata column '", group.by, "' was not found in object@meta.data."
      )
    )
  }

  if (!reduction %in% names(object@reductions)) {
    stop(
      paste0(
        "Reduction '", reduction, "' was not found in object@reductions. ",
        "Please run RunUMAP() or RunTSNE() first."
      )
    )
  }

  # -----------------------------
  # 4. Built-in CellWindX palettes
  # -----------------------------
  CellWindX_palettes <- list(

    # 中国山水画：淡雅、青绿、蓝紫、墨灰、胭脂点缀
    shanshui = c(
      "#8FB9A8", "#B7C8A3", "#A7C7D9", "#7FA6C7", "#9D9AC7",
      "#C6B7D9", "#D8C7A3", "#BFA58A", "#8A9A8F", "#6F8F9F",
      "#A6B1C2", "#C9D6DF", "#D6C6B9", "#E2D8C3", "#9E8FB2",
      "#738A99", "#B5A789", "#AFC8BA", "#D1A7A0", "#6E7F80"
    ),

    # 重庆摩登风格：夜景、霓虹、江水蓝、桥梁橙、都市灰
    chongqing_modern = c(
      "#2F4858", "#33658A", "#4A90A4", "#86BBD8", "#758E4F",
      "#F6AE2D", "#F26419", "#D64550", "#8E5572", "#5D5179",
      "#3A506B", "#1C7293", "#00A6A6", "#7D8491", "#B8B8D1",
      "#E0A458", "#C8553D", "#6D597A", "#355070", "#2A2D34"
    ),

    # 少女心：年轻、明亮、柔和、活力
    girlish = c(
      "#FF9AA2", "#FFB7B2", "#FFDAC1", "#E2F0CB", "#B5EAD7",
      "#C7CEEA", "#F6C1D5", "#D5AAFF", "#A0E7E5", "#B4F8C8",
      "#FBE7C6", "#F7A8B8", "#F3C4FB", "#BFD7FF", "#FFD6E0",
      "#FFABAB", "#FFC3A0", "#D4F0F0", "#CCE2CB", "#E7C6FF"
    )
  )

  colors_use <- CellWindX_palettes[[palette]]

  # -----------------------------
  # 5. Prepare group information
  # -----------------------------
  group_vec <- object@meta.data[[group.by]]
  group_vec <- as.factor(group_vec)
  group_levels <- levels(group_vec)
  n_groups <- length(group_levels)

  if (n_groups > length(colors_use)) {
    stop(
      paste0(
        "The selected palette supports up to ", length(colors_use),
        " groups, but '", group.by, "' contains ", n_groups, " groups. ",
        "Please provide fewer groups or extend the palette."
      )
    )
  }

  colors_use <- colors_use[seq_len(n_groups)]
  names(colors_use) <- group_levels

  # -----------------------------
  # 6. Default title
  # -----------------------------
  if (is.null(title)) {
    title <- paste0(
      toupper(reduction),
      " colored by ",
      group.by,
      " | palette: ",
      palette
    )
  }

  # -----------------------------
  # 7. Plot
  # -----------------------------
  p <- Seurat::DimPlot(
    object = object,
    reduction = reduction,
    group.by = group.by,
    cols = colors_use,
    label = label,
    repel = repel,
    pt.size = pt.size,
    label.size = label.size,
    shuffle = shuffle,
    seed = seed
  ) +
    ggplot2::ggtitle(title) +
    ggplot2::theme_classic(base_size = 14) +
    ggplot2::theme(
      plot.title = ggplot2::element_text(
        hjust = 0.5,
        face = "bold",
        size = 15
      ),
      axis.title = ggplot2::element_text(
        face = "bold",
        size = 13
      ),
      axis.text = ggplot2::element_text(
        color = "black",
        size = 11
      ),
      legend.title = ggplot2::element_blank(),
      legend.text = ggplot2::element_text(size = 11),
      legend.position = legend.position,
      panel.border = ggplot2::element_rect(
        color = "black",
        fill = NA,
        linewidth = 0.6
      ),
      axis.line = ggplot2::element_line(linewidth = 0.4),
      plot.margin = ggplot2::margin(10, 10, 10, 10)
    )

  # Seurat DimPlot 不直接支持 alpha，这里通过修改图层实现
  if (!is.null(alpha)) {
    for (i in seq_along(p$layers)) {
      if ("GeomPoint" %in% class(p$layers[[i]]$geom)) {
        p$layers[[i]]$aes_params$alpha <- alpha
      }
    }
  }

  return(p)
}


