# ============================================================
# Core analysis scripts for:
# "Integrative analysis of chemical profiling proposes sesamin as a 
#  computationally prioritized, putatively annotated candidate in 
#  SQ-DS-HQ for diabetic neuropathy"
# ============================================================
# Note: File paths and package versions may require adjustment 
# for local execution. Key parameters are documented in Methods.
# Expected directory structure:
#   ./data/   - place GSE95849_series_matrix.txt.gz and GPL22448.soft.gz here
#   ./results/ - all output CSV files will be saved here
# ============================================================

# Working directory and output paths
base_dir <- "."
data_dir <- file.path(base_dir, "data")
out_dir <- file.path(base_dir, "results")
if(!dir.exists(out_dir)) dir.create(out_dir, recursive = TRUE)

# Load required packages
library(GEOquery)
library(limma)
library(tidyverse)
library(WGCNA)
library(randomForest)
library(glmnet)
library(GSVA)
library(pROC)
library(pheatmap)
library(ggpubr)

# ============================================================
# 1. Data loading and preprocessing
# ============================================================
# Quantile normalization and log2 transformation were performed 
# via limma (see Methods)

con <- gzfile(file.path(data_dir, "GSE95849_series_matrix.txt.gz"), "r")
first_lines <- readLines(con, n = 200)
close(con)
data_start <- grep("^\\!series_matrix_table_begin", first_lines) + 1
expr_raw <- read.table(gzfile("GSE95849_series_matrix.txt.gz"), 
                       skip = data_start - 1, header = TRUE, row.names = 1,
                       check.names = FALSE, comment.char = "!", stringsAsFactors = FALSE)

# Map probes to gene symbols using GPL22448 annotation
gpl <- getGEO(filename = file.path(data_dir, "GPL22448.soft.gz"))
tab <- Table(gpl)
gene_symbols <- tab$Gene_symbol
probe2gene <- data.frame(probe = tab$ID, gene = gene_symbols, stringsAsFactors = FALSE)
probe2gene <- probe2gene[!is.na(probe2gene$gene) & probe2gene$gene != "", ]

expr_df <- as.data.frame(expr_raw)
expr_df$probe <- rownames(expr_df)
expr_annot <- inner_join(expr_df, probe2gene, by = "probe")
expr_avg <- expr_annot %>%
    group_by(gene) %>%
    summarise(across(where(is.numeric), mean, na.rm = TRUE)) %>%
    column_to_rownames("gene")

# Extract sample names and assign group labels
con <- gzfile(file.path(data_dir, "GSE95849_series_matrix.txt.gz"), "r")
all_lines <- readLines(con)
close(con)
title_line <- grep("^\\!Sample_title", all_lines, value = TRUE)[1]
sample_names <- unlist(strsplit(title_line, "\t"))[-1]
sample_names <- trimws(sample_names)
colnames(expr_avg) <- sample_names

group_vector <- case_when(
    grepl("^CN", sample_names) ~ "Healthy",
    grepl("^DM", sample_names) ~ "DM",
    grepl("^DPN", sample_names) ~ "DPN",
    TRUE ~ NA_character_
)
names(group_vector) <- sample_names

# ============================================================
# 2. Differential expression analysis (limma)
# ============================================================
# Comparisons: DM vs Healthy, DPN vs Healthy, DPN vs DM
# Thresholds: |log2FC| > 1, adjusted P-value < 0.05

group_factor <- factor(group_vector, levels = c("Healthy", "DM", "DPN"))
design <- model.matrix(~0 + group_factor)
colnames(design) <- levels(group_factor)

fit <- lmFit(expr_avg, design)
contrast_matrix <- makeContrasts(
    DM_vs_Healthy = DM - Healthy,
    DPN_vs_Healthy = DPN - Healthy,
    DPN_vs_DM = DPN - DM,
    levels = design
)
fit2 <- contrasts.fit(fit, contrast_matrix)
fit2 <- eBayes(fit2)

res_DM <- topTable(fit2, coef = "DM_vs_Healthy", number = Inf, adjust.method = "BH")
res_DPN <- topTable(fit2, coef = "DPN_vs_Healthy", number = Inf, adjust.method = "BH")
res_DPNvsDM <- topTable(fit2, coef = "DPN_vs_DM", number = Inf, adjust.method = "BH")

for(res in list(res_DM, res_DPN, res_DPNvsDM)) {
    res$diff <- "Not Sig"
    res$diff[res$logFC > 1 & res$adj.P.Val < 0.05] <- "Up"
    res$diff[res$logFC < -1 & res$adj.P.Val < 0.05] <- "Down"
}

write.csv(res_DM, file.path(out_dir, "DEG_DM_vs_Healthy.csv"))
write.csv(res_DPN, file.path(out_dir, "DEG_DPN_vs_Healthy.csv"))
write.csv(res_DPNvsDM, file.path(out_dir, "DEG_DPN_vs_DM.csv"))

# Complication-specific genes: significant in DPN vs DM, 
# but not significant in DM vs Healthy
sig_DPNvsDM <- res_DPNvsDM[res_DPNvsDM$adj.P.Val < 0.05 & abs(res_DPNvsDM$logFC) > 1, ]
not_sig_DM <- res_DM[res_DM$adj.P.Val >= 0.05 | abs(res_DM$logFC) < 1, ]
specific_genes <- intersect(rownames(sig_DPNvsDM), rownames(not_sig_DM))
write.csv(data.frame(Gene = specific_genes), file.path(out_dir, "Core_Complication_Specific_Genes_All.csv"), row.names = FALSE)

# ============================================================
# 3. WGCNA network construction and module selection
# ============================================================
# Soft-threshold power = 13, mergeCutHeight = 0.25

datExpr <- as.data.frame(t(expr_avg))
powers <- c(1:20)
sft <- pickSoftThreshold(datExpr, powerVector = powers, verbose = 5)
softPower <- 13  

net <- blockwiseModules(
    datExpr,
    power = softPower,
    maxBlockSize = 20000,
    TOMType = "unsigned",
    minModuleSize = 30,
    reassignThreshold = 0,
    mergeCutHeight = 0.25,
    numericLabels = TRUE,
    verbose = 3
)

# Module–trait correlation
MEs <- moduleEigengenes(datExpr, net$colors)$eigengenes
trait <- data.frame(
    DPN = ifelse(group_vector == "DPN", 1, 0),
    DM = ifelse(group_vector == "DM", 1, 0),
    Healthy = ifelse(group_vector == "Healthy", 1, 0)
)
rownames(trait) <- rownames(datExpr)
moduleTraitCor <- cor(MEs, trait, use = "p")
moduleTraitPvalue <- corPvalueStudent(moduleTraitCor, nrow(datExpr))

# Select modules significantly associated with DPN (|cor| > 0.6, P < 0.05)
DPN_cor <- moduleTraitCor[, "DPN"]
DPN_pval <- moduleTraitPvalue[, "DPN"]
selected_modules <- names(DPN_cor)[abs(DPN_cor) > 0.6 & DPN_pval < 0.05]

module_genes <- unlist(sapply(selected_modules, function(mod) {
    mod_num <- as.numeric(gsub("ME", "", mod))
    names(net$colors[net$colors == mod_num])
}))
write.csv(data.frame(Gene = module_genes), file.path(out_dir, "WGCNA_DPN_Modules_Genes.csv"), row.names = FALSE)

# ============================================================
# 4. Intersection of three gene sets
#    (complication-specific ∩ WGCNA modules ∩ drug–disease targets)
# ============================================================
# Drug–disease overlapping targets are from Supplementary File S4 
# (column SQ-DS-HQ|DPN, 321 targets).
# drug_disease_genes <- read.csv("Supplementary_File_S4.csv")$SQ.DS.HQ.DPN
# core_genes <- intersect(intersect(specific_genes, module_genes), drug_disease_genes)
# This intersection yielded 20 candidate genes for machine learning.

# Load the 20 candidate genes for downstream analysis
core_genes <- read.csv(file.path(out_dir, "Final_Core_Genes_Triple_Intersection.csv"))$Gene
expr_core <- expr_avg[rownames(expr_avg) %in% core_genes, ]
data_rf <- as.data.frame(t(expr_core))
data_rf$Group <- ifelse(group_vector[rownames(data_rf)] == "DPN", "DPN", "NonDPN")
data_rf$Group <- factor(data_rf$Group, levels = c("NonDPN", "DPN"))

# ============================================================
# 5. Random forest feature importance ranking
# ============================================================
# seed = 123, ntree = 500

set.seed(123)
trainIndex <- createDataPartition(data_rf$Group, p = 0.7, list = FALSE)
trainData <- data_rf[trainIndex, ]
testData <- data_rf[-trainIndex, ]

rf_model <- randomForest(Group ~ ., data = trainData, ntree = 500, importance = TRUE)
importance_df <- as.data.frame(importance(rf_model))
importance_df$Gene <- rownames(importance_df)
importance_df <- importance_df[order(importance_df$MeanDecreaseGini, decreasing = TRUE), ]
top10_genes <- head(importance_df$Gene, 10)
write.csv(importance_df, file.path(out_dir, "RF_Gene_Importance.csv"), row.names = FALSE)

# ============================================================
# 6. Elastic net (LASSO) regression
# ============================================================
# alpha = 0.8, 10-fold cross-validation

x <- as.matrix(data_rf[, -ncol(data_rf)])
y <- as.numeric(data_rf$Group) - 1  # DPN = 1, NonDPN = 0
set.seed(789)
cv_lasso <- cv.glmnet(x, y, family = "binomial", alpha = 0.8, nfolds = 10)
coef_min <- coef(cv_lasso, s = "lambda.min")
lasso_genes <- rownames(coef_min)[which(coef_min != 0)][-1]  # exclude intercept

# Intersection of RF top 10 and LASSO non-zero genes
final_genes <- intersect(top10_genes, lasso_genes)
# Eight prioritized genes: TH, SIGMAR1, PTPN22, AUH, PDE1A, P2RY12, JAK2, MTNR1B
write.csv(data.frame(Gene = final_genes), file.path(out_dir, "Final_ML_Core_Genes_RF_LASSO.csv"), row.names = FALSE)

# ============================================================
# 7. Immune infiltration analysis (ssGSEA)
# ============================================================
# In the final analysis, the GSVA package (method = "ssgsea") 
# was used as described in Methods. The manual averaging approach 
# shown here yields comparable results.

immune_signatures <- list(
    "CD8+ T cells" = c("CD8A","CD8B","GZMK","PRF1"),
    "CD4+ T cells" = c("CD4","IL2RA","ICOS","CD40LG"),
    "Regulatory T cells" = c("FOXP3","IL2RA","CTLA4","TNFRSF18"),
    "Gamma delta T cells" = c("TRDC","TRGC2","TRDV1"),
    "NK cells" = c("NCAM1","KLRK1","NCR1","KIR2DL3"),
    "B cells" = c("CD19","MS4A1","CD79A","CD79B"),
    "Plasma cells" = c("SDC1","XBP1","IRF4"),
    "Macrophages" = c("CD68","CD163","MSR1","ITGAM"),
    "M1 Macrophages" = c("NOS2","IRF5","TNF"),
    "M2 Macrophages" = c("CD163","MSR1","ARG1"),
    "Dendritic cells" = c("CD1C","ITGAX","FLT3"),
    "Myeloid dendritic cells" = c("CD1C","ITGAM","CLEC4C"),
    "Plasmacytoid dendritic cells" = c("CLEC4C","IL3RA","GZMB"),
    "Monocytes" = c("CD14","FCGR3A","LYZ"),
    "Neutrophils" = c("FCGR3B","CEACAM8","ELANE"),
    "Eosinophils" = c("IL5RA","CCR3","PRG2"),
    "Mast cells" = c("KIT","MS4A2","TPSAB1")
)

available_genes <- rownames(expr_avg)
filtered_signatures <- lapply(immune_signatures, function(genes) intersect(genes, available_genes))
filtered_signatures <- filtered_signatures[sapply(filtered_signatures, length) > 0]

expr_matrix <- as.matrix(expr_avg)
mean_scores <- sapply(filtered_signatures, function(genes) {
    colMeans(expr_matrix[genes, , drop = FALSE], na.rm = TRUE)
})
immune_matrix <- as.data.frame(t(mean_scores))
immune_matrix$Group <- group_vector[rownames(immune_matrix)]

# ============================================================
# 8. ROC curves (descriptive, training set only)
# ============================================================
target_genes <- final_genes  
expr_target <- t(expr_avg[rownames(expr_avg) %in% target_genes, ])
y_binary <- ifelse(group_vector[rownames(expr_target)] == "DPN", 1, 0)

# Single-gene ROC
for(gene in target_genes) {
    roc_obj <- roc(y_binary, expr_target[, gene], direction = "<")
    auc_val <- auc(roc_obj)
}

# Combined logistic regression model was removed from the revised manuscript
# df_logit <- data.frame(y = y_binary, expr_target)
# model <- glm(y ~ ., data = df_logit, family = binomial())
# pred_prob <- predict(model, type = "response")
# roc_combined <- roc(y_binary, pred_prob)
# auc_combined <- auc(roc_combined)

# ============================================================
cat("Core analysis script completed.\n")
sessionInfo()