#' Convert MultiAssayExperiment to TreatmentResponseExperiment
#'
#' This function converts a MultiAssayExperiment generated by gDR into a TreatmentResponseExperiment for use in the
#' PharmacoGx package. The resulting PharmacoSet can be used for pharmacogenomic analysis of drug response.
#'
#' @param mae A MultiAssayExperiment object generated by gDR.
#' @param pset_name A character string specifying the name of the resulting PharmacoSet object.
#'
#' @keywords pset_conversion
#'
#' @return A PharmacoSet object.
#'
#' @examples
#' # Convert a MultiAssayExperiment object to a PharmacoSet object
#' m <- 20
#' n <- 10
#' rnames <- LETTERS[1:m]
#' cnames <- letters[1:n]
#' ref_gr_value <-  matrix(runif(m * n), nrow = m, ncol = n, dimnames = list(rnames, cnames))
#' se <- SummarizedExperiment::SummarizedExperiment(assays = list(RefGRvalue = ref_gr_value),
#'                                                  rowData = S4Vectors::DataFrame(rnames),
#'                                                  colData = S4Vectors::DataFrame(cnames))
#' mae <- MultiAssayExperiment::MultiAssayExperiment(experiments = list("single-agent" = se))
#' convert_MAE_to_PSet(mae, "my_pset")
#'
#' @export
convert_MAE_to_PSet <- function(mae,
                                pset_name) {
    checkmate::assert_class(mae, "MultiAssayExperiment")
    checkmate::assert_string(pset_name)

    # Get the unique assay names from all experiments combined
    assay_names <- unique(unlist(gDRutils::MAEpply(mae, function(x) SummarizedExperiment::assayNames(x))))

    # Create a list of data.tables, one for each assay name, with the data from all Summarized Experiments in the MAE.
    assay_list <- lapply(assay_names, function(x) {

        # Get list of all the Summarized Experiments with all the unique Assays across the entire MAE
        # If the summarized experiment does not have a given assay, create it
        se_list <- gDRutils::MAEpply(mae = mae, FUN = .extract_or_create_assay, unify = FALSE, x)

        # Get list of datatables with all assays with name=x converted to a data.table
        se_dt_list <- lapply(se_list, FUN = gDRutils::convert_se_assay_to_dt, x)

        # Combine all the data.tables into one data.table, add the name of the Summarized Experiment
        # as a "data_type" column so that users can identify which Summarized Experiment the data came from
        DT <- data.table::rbindlist(se_dt_list, fill = TRUE, idcol = "data_type")

        # Change column names
        data.table::setnames(DT, "rId", "treatmentid")
        data.table::setnames(DT, "cId", "sampleid")
    })

    # Set the names of the list of data.tables to the assay names so that they can be accessed by name later
    names(assay_list) <- assay_names

    # Combine all the data.tables into one data.table
    assay_dt <- data.table::rbindlist(assay_list, fill = TRUE, idcol = "assay_name")

    # Get the unique rownames from all experiments combined
    rownames <- unique(unlist(gDRutils::MAEpply(mae, FUN = rownames)))
    # convert the rowData of each Summarized Experiment to a data.table and combine them into one data.table
    rowdata_ <- gDRutils::MAEpply(
      mae = mae,
      FUN = function(x) data.table::as.data.table(data.frame(SummarizedExperiment::rowData(x)), keep.rownames = TRUE),
      unify = TRUE)
    # rename the rownames column to "treatmentid" (required for PharmacoSet Object)
    data.table::setnames(rowdata_, "rn", "treatmentid")


    # Get the unique colnames from all experiments combined
    colnames <- unique(unlist(gDRutils::MAEpply(mae, FUN = colnames)))
    # convert the colData of each Summarized Experiment to a data.table and combine them into one data.table
    coldata_ <- unique(gDRutils::MAEpply(
      mae = mae,
      FUN = function(x) data.table::as.data.table(data.frame(SummarizedExperiment::colData(x)), keep.rownames = TRUE),
      unify = TRUE))
    # rename the rownames column to "sampleid" (required for PharmacoSet Object)
    data.table::setnames(coldata_, "rn", "sampleid")

    # Create a list of rowname and column name identifiers for each assay name (required for
    # TreatmentResponseExperiment object)
    # in this case, all the identifiers are the same treatmentid & sampleid columns
    assayIDs <- stats::setNames(lapply(assay_names, function(x) c("treatmentid", "sampleid")), assay_names)

    tre <- CoreGx::TreatmentResponseExperiment(
        rowData = rowdata_,
        colData = coldata_,
        rowIDs = "treatmentid",
        colIDs = "sampleid",
        assays = assay_list,
        assayIDs = assayIDs,
        metadata = list()
    )

    # add batchid column to coldata_ (required for PSet)
    coldata_$batchid <- 1

    # Create PharmacoSet object (implicit return)
    # The molecularProfiles slot is empty because the gDR object does not contain molecular profiles
    # As of this version, curation slot is left empty.
    PharmacoGx::PharmacoSet2(
        name = pset_name,
        treatment = as.data.frame(rowdata_), # nolint
        sample = as.data.frame(coldata_), # nolint
        molecularProfiles =
            MultiAssayExperiment::MultiAssayExperiment(
                MultiAssayExperiment::ExperimentList(
                    emptySE = SummarizedExperiment::SummarizedExperiment(
                        assays = list(),
                        colData = coldata_
                ))),
        treatmentResponse = tre,
        perturbation = list(),
        curation = list(sample = data.frame(),
                        treatment = data.frame(),
                        tissue = data.frame()),
        datasetType = "sensitivity")

}


#' Extracts an assay from a SummarizedExperiment object or creates a new one if it does not exist
#'
#' This function takes a SummarizedExperiment object and an assay name as input.
#' If the specified assay already exists in the SummarizedExperiment object, it is returned.
#' Otherwise, a new assay with the specified name is created and added to the SummarizedExperiment object.
#' The new assay is initialized with NA values.
#' This is useful for when multiple Summarized Experiments in a given MAE do not have the same assays.
#' And it is necessary to have the same assays in all Summarized Experiments in order to convert the MAE to a PSet.
#'
#' @param SE A SummarizedExperiment object
#' @param assay_name A character string specifying the name of the assay to extract or create
#'
#' @keywords internal
#'
#' @return A SummarizedExperiment object with the specified assay
#'
.extract_or_create_assay <- function(SE,
                                     assay_name) {
    checkmate::assert_class(SE, "SummarizedExperiment")
    checkmate::assert_string(assay_name)
    if (assay_name %in% SummarizedExperiment::assayNames(SE)) {
        SE
    } else {
        SummarizedExperiment::assay(SE, assay_name) <-
            BumpyMatrix::splitAsBumpyMatrix(
                S4Vectors::DataFrame(x = rep(NA, prod(dim(SE)))),
                row = rownames(SE),
                column = colnames(SE)
            )
        SE
    }
}
