#' @title 3D Surface Plot for QQKRLS
#'
#' @param qqkrls_result A \code{qqkrls} object.
#' @param value Column to plot. Default \code{"coefficient"}.
#' @param colorscale Default \code{"Parula"}.
#' @param show_contour Show gridlines on the surface.
#' @param x_label,y_label,title Axis labels and title.
#' @return A plotly object.
#' @export
#' @importFrom plotly plot_ly layout "%>%"
plot_qqkrls_3d <- function(qqkrls_result, value = "coefficient",
                           colorscale = "Parula", show_contour = TRUE,
                           x_label = "X Quantile (theta)",
                           y_label = "Y Quantile (tau)",
                           title = NULL) {
  M <- qqkrls_to_matrix(qqkrls_result, value)
  xs <- as.numeric(colnames(M)); ys <- as.numeric(rownames(M))
  zlab <- switch(value, coefficient = "beta(theta, tau)",
                 std_error = "SE",
                 t_value = "t-value",
                 p_value = "p-value", value)
  if (is.null(title)) title <- paste("QQKRLS 3D Surface -", zlab)
  cs <- resolve_colorscale(colorscale)
  step_x <- if (length(xs) > 1) diff(xs)[1] else 0.05
  step_y <- if (length(ys) > 1) diff(ys)[1] else 0.05
  plotly::plot_ly(
    x = xs, y = ys, z = M, type = "surface",
    colorscale = cs, showscale = TRUE,
    colorbar = list(title = zlab, tickformat = ".3f"),
    contours = list(
      x = list(show = show_contour, color = "black",
               start = min(xs), end = max(xs), size = step_x),
      y = list(show = show_contour, color = "black",
               start = min(ys), end = max(ys), size = step_y),
      z = list(show = FALSE)),
    lighting = list(ambient = 0.55, diffuse = 0.8,
                    specular = 0.15, roughness = 0.9)
  ) %>%
    plotly::layout(title = title,
                   paper_bgcolor = "white", plot_bgcolor = "white",
                   scene = list(
                     xaxis = list(title = x_label, tickformat = ".2f",
                                  showgrid = TRUE, gridcolor = "#E6E6E6"),
                     yaxis = list(title = y_label, tickformat = ".2f",
                                  showgrid = TRUE, gridcolor = "#E6E6E6"),
                     zaxis = list(title = zlab, showgrid = TRUE,
                                  gridcolor = "#F0F0F0"),
                     aspectratio = list(x = 1, y = 1, z = 0.7),
                     camera = list(eye = list(x = 1.4, y = 1.7, z = 1.2))))
}


#' @title Heatmap for QQKRLS
#' @param qqkrls_result A qqkrls object.
#' @param value Column to plot.
#' @param colorscale Default \code{"Parula"}.
#' @param show_stars Overlay significance stars.
#' @param x_label,y_label,title See \code{\link{plot_qqkrls_3d}}.
#' @return A plotly object.
#' @export
plot_qqkrls_heatmap <- function(qqkrls_result, value = "coefficient",
                                colorscale = "Parula", show_stars = FALSE,
                                x_label = "X Quantile (theta)",
                                y_label = "Y Quantile (tau)",
                                title = NULL) {
  M <- qqkrls_to_matrix(qqkrls_result, value)
  xs <- as.numeric(colnames(M)); ys <- as.numeric(rownames(M))
  zlab <- switch(value, coefficient = "beta(theta, tau)",
                 std_error = "SE", t_value = "t-value",
                 p_value = "p-value", value)
  if (is.null(title)) title <- paste("QQKRLS Heatmap -", zlab)
  cs <- resolve_colorscale(colorscale)
  p <- plotly::plot_ly(x = xs, y = ys, z = M, type = "heatmap",
                       colorscale = cs, showscale = TRUE,
                       hovertemplate = paste0("theta: %{x:.2f}<br>tau: %{y:.2f}<br>",
                                              zlab, ": %{z:.4f}<extra></extra>")) %>%
    plotly::layout(title = title,
                   xaxis = list(title = x_label),
                   yaxis = list(title = y_label))
  if (show_stars && value == "coefficient") {
    pmat <- qqkrls_to_matrix(qqkrls_result, "p_value")
    stars <- matrix("", nrow = nrow(pmat), ncol = ncol(pmat))
    stars[pmat < 0.10] <- "*"; stars[pmat < 0.05] <- "**"; stars[pmat < 0.01] <- "***"
    anns <- list()
    for (i in seq_len(nrow(M))) for (j in seq_len(ncol(M))) {
      if (nzchar(stars[i, j])) {
        anns[[length(anns) + 1]] <- list(
          x = xs[j], y = ys[i], text = stars[i, j],
          xref = "x", yref = "y", showarrow = FALSE,
          font = list(size = 10, color = "white"))
      }
    }
    p <- p %>% plotly::layout(annotations = anns)
  }
  p
}


#' @title Contour Plot for QQKRLS
#' @param qqkrls_result A qqkrls object.
#' @param value,colorscale,x_label,y_label,title See \code{\link{plot_qqkrls_3d}}.
#' @return A plotly object.
#' @export
plot_qqkrls_contour <- function(qqkrls_result, value = "coefficient",
                                colorscale = "Parula",
                                x_label = "X Quantile (theta)",
                                y_label = "Y Quantile (tau)",
                                title = NULL) {
  M <- qqkrls_to_matrix(qqkrls_result, value)
  xs <- as.numeric(colnames(M)); ys <- as.numeric(rownames(M))
  zlab <- switch(value, coefficient = "beta(theta, tau)",
                 p_value = "p-value", value)
  if (is.null(title)) title <- paste("QQKRLS Contour -", zlab)
  cs <- resolve_colorscale(colorscale)
  plotly::plot_ly(x = xs, y = ys, z = M, type = "contour",
                  colorscale = cs, showscale = TRUE,
                  contours = list(showlabels = TRUE)) %>%
    plotly::layout(title = title,
                   xaxis = list(title = x_label),
                   yaxis = list(title = y_label))
}


#' @title S3 plot method for qqkrls
#' @param x A qqkrls object.
#' @param value Column to plot.
#' @param colorscale Default \code{"Parula"}.
#' @param kind \code{"3d"}, \code{"heatmap"}, or \code{"contour"}.
#' @param ... Passed to the underlying plot function.
#' @return A plotly object.
#' @export
plot.qqkrls <- function(x, value = "coefficient", colorscale = "Parula",
                       kind = c("3d", "heatmap", "contour"), ...) {
  kind <- match.arg(kind)
  fn <- switch(kind, "3d" = plot_qqkrls_3d,
               heatmap = plot_qqkrls_heatmap,
               contour = plot_qqkrls_contour)
  fn(x, value = value, colorscale = colorscale, ...)
}
