# Section: Capsule Import and GSEA Result Extraction ----



#' @title Load and Smart-Relocate GSEA Computation Capsule

#' @description Safely load a computation capsule. If the file is moved from its

#'   original project path (e.g., copied to desktop), the engine will automatically

#'   restore the standard folder structure in the current working directory and

#'   relocate the capsule. Fully compatible with GseaEnv and GseaRes objects.

#' @param file_path Character. Absolute or relative path to the capsule rds file.

#' @param auto_relocate Logical. Whether to automatically fix the folder structure

#'   and relocate based on the built-in lineage in the current directory? Default TRUE.

#' @param inspect Logical. Whether to automatically print capsule overview after

#'   loading? Default TRUE.

#' @return Returns the parsed GseaRes or GseaEnv capsule object.

#' @export



#' @examples
#' # Load the pre-computed GseaRes capsule shipped with the package, write it
#' # to a temp file, then import it (auto_relocate = FALSE to avoid side
#' # effects).
#' data(precomputed_gseares, package = "GSEAlens")
#' tmp_capsule <- file.path(tempdir(), "import_demo.rds")
#' saveRDS(precomputed_gseares, tmp_capsule)
#'
#' gsea_res <- import_gsea_capsule(
#'   tmp_capsule,
#'   auto_relocate = FALSE,
#'   inspect = FALSE
#' )
#' class(gsea_res)
import_gsea_capsule <- function(file_path, auto_relocate = TRUE, inspect = TRUE) {

    # 1. 文件存在性检查

    if (!file.exists(file_path)) {

    stop(sprintf("File does not exist: %s", file_path))

    }



    message("Waking up computation capsule...")

    capsule <- readRDS(file_path)



    # 2. 识别胶囊类型

    if (inherits(capsule, c("GseaEnv", "GseaEnvPro"))) {

    message("Successfully loaded [GseaEnv] environment encapsulation capsule (parallel computation not yet performed).")



    if (inspect && exists("inspect_gsea_env", mode = "function")) {

            inspect_gsea_env(capsule)

    }



    return(invisible(capsule))

    }



    # 3. 对于计算结果胶囊，执行智能归位逻辑

    if (!inherits(capsule, "GseaRes")) {

    warning("This file does not appear to be a standard GSEAlens capsule!")

    return(invisible(capsule))

    }



    # 4. 检查是否有系统记忆（project_info）

    project_info <- capsule$metadata$project_info



    if (is.null(project_info)) {

    message("This capsule was generated by an older version and lacks project lineage memory. Loading directly (auto-relocation not possible).")

    } else {

    # 5. 获取当前路径与预期路径

    current_abs <- normalizePath(file_path, winslash = "/", mustWork = FALSE)

    expected_abs <- normalizePath(project_info$rds_path, winslash = "/", mustWork = FALSE)



    # 6. 路径不一致，且开启了自动归位

    if (current_abs != expected_abs && auto_relocate) {

            message("[Lineage Alert] Capsule is currently in a non-standard path")

            message(sprintf("   Current location: %s", current_abs))

            message(sprintf("   Original project: [%s]", project_info$custom_series_name))



            # 7. 根据当前工作目录重建标准档案库

            local_series_dir <- file.path(getwd(), "GSEA_Output", project_info$custom_series_name)

            if (!dir.exists(local_series_dir)) {

        dir.create(local_series_dir, recursive = TRUE, showWarnings = FALSE)

        message(sprintf("   Created standard archive directory: %s", local_series_dir))

            }



            # 8. 执行文件归位

            target_file <- file.path(local_series_dir, basename(file_path))



            if (!file.exists(target_file) || target_file == current_abs) {

        file.copy(from = file_path, to = target_file, overwrite = TRUE)

        message("   Automatically relocated capsule to standard archive")

        message(sprintf("   New location: %s", target_file))



        # 9. 更新胶囊内部的路径信息，防止下次加载时重复警告

        capsule$metadata$project_info$output_dir <- file.path(getwd(), "GSEA_Output")

        capsule$metadata$project_info$series_dir <- local_series_dir

        capsule$metadata$project_info$rds_path <- target_file

            } else {

        message("   Backup already exists in standard archive (no duplicate needed).")

            }

    }

    }



    # 10. 自动调用探针函数打印概况

    if (inspect) {

    if (exists("inspect_gsea_res", mode = "function")) {

            inspect_gsea_res(capsule)

    } else {

            message("Successfully loaded [GseaRes] computation result capsule!")

    }

    }



    return(invisible(capsule))

}





#' @title Extract GSEA Results for Specific Contrast

#' @description Extract results for a specified contrast from a GseaRes capsule.

#'   Supports subsetting by gene set collection and automatically recalculates FDR.

#' @param gsea_res GseaRes object (generated by batch_calc_gsea)

#' @param contrast_id String. Contrast group ID (e.g., "A_vs_B").

#' @param target_collection String vector. Gene set collection to extract

#'   (e.g., "H", "C2:CP:KEGG_LEGACY"). Default "ALL" means extract all.

#' @return GseaTask object.

#' @examples

#' # Load a pre-computed GseaRes shipped with the package

#' data(precomputed_gseares, package = "GSEAlens")
#'
#' gsea_res <- precomputed_gseares

#' task <- extract_gsea_task(gsea_res, contrast_id = "untrt_vs_trt")

#' class(task)

#' @export

extract_gsea_task <- function(gsea_res, contrast_id, target_collection = "ALL") {

    # 1. 校验输入

    .check_gsea_res(gsea_res)



    if (!(contrast_id %in% names(gsea_res$results))) {

    stop(sprintf(

            "Contrast '%s' not found in results. Available: %s",

            contrast_id, paste(names(gsea_res$results), collapse = ", ")

    ))

    }



    task_info <- gsea_res$results[[contrast_id]]



    if (task_info$status != "Success") {

    stop(sprintf("Contrast '%s' computation failed or has no enrichment results, cannot extract.", contrast_id))

    }



    # 2. 获取元信息（从 registry 中精确匹配，不再解析字符串）

    # 注意：contrast_id 可能是反向的 (B_vs_A)，registry 中可能只有正向的 (A_vs_B)

    # 但我们在 batch_calc_gsea 中已经把反向任务写入了 results，却没有写入 registry (这是设计上的权衡，registry 记录原始生物学对比)

    # 这里我们需要智能处理 left/right group



    # 优先从 results 里的 genelist 名称推断 (最准确)

    # 或者从 registry 查找



    reg <- gsea_res$contrast_registry

    reg_match <- reg[reg$contrast_id == contrast_id, ]



    left_group <- NA

    right_group <- NA



    if (nrow(reg_match) > 0) {

    left_group <- reg_match$left_group[1]

    right_group <- reg_match$right_group[1]

    } else {

    # 如果 registry 里没找到 (可能是反向生成的任务)，尝试从 ID 解析

    parts <- strsplit(contrast_id, "_vs_")[[1]]

    if (length(parts) == 2) {

            left_group <- parts[1]

            right_group <- parts[2]

    } else {

            left_group <- contrast_id

            right_group <- "Background"

    }

    }



    # 3. 提取 GSEA 结果对象

    gsea_obj <- task_info$data

    res_df <- as.data.frame(gsea_obj@result)



    # 4. 切片逻辑

    is_slice_mode <- length(target_collection) != 1 || toupper(target_collection[1]) != "ALL"



    if (is_slice_mode) {

    # 查找匹配的行

    # 支持 Collection (如 "H") 或 Combo_Name (如 "C2:CP:KEGG_LEGACY")

    match_idx <- which(res_df$Collection %in% target_collection |

            res_df$Combo_Name %in% target_collection)



    if (length(match_idx) == 0) {

            stop(sprintf(

        "Slice failed! No pathways found for '%s' in '%s'.",

        contrast_id, paste(target_collection, collapse = ", ")

            ))

    }



    res_df <- res_df[match_idx, , drop = FALSE]



    # 关键：重算 FDR (消除全库背景的惩罚)

    res_df$p.adjust <- p.adjust(res_df$pvalue, method = "BH")

    if ("qvalue" %in% colnames(res_df)) res_df$qvalue <- res_df$p.adjust



    message(sprintf("[Smart Slice] Extracted %d pathways. FDR recalculated.", nrow(res_df)))



    # 更新 S4 对象

    gsea_obj@result <- res_df

    }



    # 5. 构建 GseaTask 对象

    # 整合 meta 信息，供下游绘图使用

    task_meta <- list(

    contrast_id = contrast_id,

    left_group = left_group,

    right_group = right_group,

    geneset_name = ifelse(is_slice_mode,

            paste(target_collection, collapse = "_"),

            gsea_res$geneset_info$name

    ),

    meta_dict = gsea_res$geneset_info$meta_dict,

    expr_bundle = gsea_res$expr_bundle,

    project_info = gsea_res$metadata$project_info,

    backend_info = gsea_res$backend_info

    )



    obj <- create_gsea_task(gsea_res = gsea_obj, meta = task_meta)



    return(obj)

}





# Section: Inspection Functions ----



#' @title View GSEA Result Overview

#' @description Print detailed summary of GseaRes object to console.

#' @param gsea_res GseaRes object

#' @return Invisibly returns the input object (for chaining).

#' @export



#' @examples

#' # Load a pre-computed GseaRes shipped with the package

#' data(precomputed_gseares, package = "GSEAlens")
#'
#' gsea_res <- precomputed_gseares

#' inspect_gsea_res(gsea_res)

inspect_gsea_res <- function(gsea_res) {

    if (!inherits(gsea_res, "GseaRes")) stop("Input object is not of GseaRes class.")



    bi <- gsea_res$backend_info

    results <- gsea_res$results



    message("")

    message(paste(rep("=", 60), collapse = ""))

    message("       GSEAlens Result Summary")

    message(paste(rep("=", 60), collapse = ""))



    # 1. 后端信息

    message("[1] Backend")

    message(sprintf("   Type       : %s", bi$backend))

    message(sprintf("   Run Time   : %s", gsea_res$metadata$run_time))

    message("")



    # 2. 计算状态统计

    message("[2] Calculation Status")

    status_count <- table(vapply(results, function(x) x$status, character(1)))

    for (name in names(status_count)) {

    icon <- if (name == "Success") "OK" else "FAIL"

    message(sprintf("   %s %s : %d", icon, name, status_count[[name]]))

    }

    message("")



    # 3. 详细列表，修改为全部显示

    message("[3] Contrast Details ")

    n_show <- length(results)

    for (i in seq_len(n_show)) {

    task_name <- names(results)[i]

    task <- results[[i]]



    # IRON FIX (2026-07-01, B-7): defensive S4/data.frame dispatch on task$data
    # so the loop survives edge cases (NULL, plain data.frame, or non-gseaResult S4).
    if (task$status == "Success") {

            n_sig <- if (methods::is(task$data, "gseaResult")) {
        sum(task$data@result$p.adjust < 0.05, na.rm = TRUE)
            } else if (is.data.frame(task$data) && "p.adjust" %in% colnames(task$data)) {
        sum(task$data$p.adjust < 0.05, na.rm = TRUE)
            } else if (is.data.frame(task$data) && "padj" %in% colnames(task$data)) {
        sum(task$data$padj < 0.05, na.rm = TRUE)
            } else {
        NA_integer_
            }

            message(sprintf("   [%d] %-20s | FDR<0.05: %s", i, task_name,
                                            if (is.na(n_sig)) "n/a" else as.character(n_sig)))

    } else {

            message(sprintf("   [%d] %-20s | Failed", i, task_name))

    }

    }

    message("")



    # 4. 下一步指引

    message("[4] Next Step")

    message("   Extract specific results for visualization:")

    message(sprintf('   > task <- extract_gsea_task(gsea_res, "%s")', names(results)[1]))

    message("   > plot_directional_gsea(task, ...)")

    message(paste(rep("=", 60), collapse = ""))



    invisible(gsea_res)

}



#' @export

print.GseaRes <- function(x, ...) {

    inspect_gsea_res(x)

}

