#' Generalized Sierpiński graph
#'
#' Constructs the generalized Sierpiński graph \eqn{S(n, G)} as defined in
#' "Generalized Sierpiński graphs" (Gravier et al.). The vertex set is
#' \eqn{\{1,\dots,k\}^n} where \eqn{k = |V(G)|}, and edges are defined
#' recursively based on the edges of \eqn{G}.
#'
#' @param G An igraph object representing the base graph. Its vertices must be
#'        labelled \code{1, 2, ..., k} (as characters or integers).
#' @param n Integer, dimension (\eqn{n \ge 1}). \code{n = 1} returns \code{G}.
#'
#' @return An igraph object representing \eqn{S(n, G)}.
#'
#' @import igraph
#' @export
#' @references
#' Gravier, Sylvain, Matjaž Kovše, and Aline Parreau. "Generalized Sierpiński graphs." Posters at EuroComb 11 (2011): 2016-0216.
#' @examples
#' library(igraph)
#'
#' # Example 1: Base graph is a 4-cycle
#' G <- make_ring(4)
#' S2 <- generalized_sierpinski(G, 2)
#' plot(S2, vertex.size = 8, vertex.label = NA)
#'
#' # Example 2: Base graph is a triangle (classical Sierpiński gasket)
#' G_tri <- make_graph(~ 1-2-3-1)
#' S3_tri <- generalized_sierpinski(G_tri, 3)
#' summary(S3_tri)  # vertices: 27, edges: 63
#'
#' # Example 3: Base graph is a complete graph K4
#' G_K4 <- make_full_graph(4)
#' S2_K4 <- generalized_sierpinski(G_K4, 2)
#' summary(S2_K4)  # vertices: 16, edges: 96
generalized_sierpinski <- function(G, n) {
  if (!inherits(G, "igraph")) stop("G must be an igraph object")
  if (n < 1) stop("n must be >= 1")

  k <- igraph::vcount(G)
  if (n == 1) {
    return(G)
  }

  # Generate all vertex labels (strings of length n, characters from 1:k)
  vertices <- as.matrix(expand.grid(rep(list(1:k), n)))
  vertices <- apply(vertices, 1, paste, collapse = "")

  # Get edges of G (undirected, using vertex indices)
  edges_G <- igraph::as_edgelist(G, names = FALSE)

  # Store edges as character pairs
  edge_list <- list()

  for (i in seq_len(n)) {
    # Generate all prefixes of length i-1
    if (i == 1) {
      prefixes <- c("")
    } else {
      prefix_mat <- as.matrix(expand.grid(rep(list(1:k), i - 1)))
      prefixes <- apply(prefix_mat, 1, paste, collapse = "")
    }

    for (w in prefixes) {
      for (e in seq_len(nrow(edges_G))) {
        x <- edges_G[e, 1]
        y <- edges_G[e, 2]

        # Suffix: repeat y or x for n-i times
        suffix1 <- paste(rep(y, n - i), collapse = "")
        suffix2 <- paste(rep(x, n - i), collapse = "")

        u <- paste0(w, x, suffix1)
        v <- paste0(w, y, suffix2)

        edge_list <- c(edge_list, list(c(u, v)))
      }
    }
  }

  # Combine and deduplicate edges
  edges_mat <- do.call(rbind, edge_list)
  edges_mat <- unique(edges_mat)

  # Build igraph object
  g <- igraph::graph_from_edgelist(edges_mat, directed = FALSE)
  return(g)
}
