///|
fn muscat_demo_config() -> @bio.MuscatAdvancedConfig {
  @bio.MuscatAdvancedConfig::create(
    min_cells=3,
    min_count=1.0,
    min_samples=2,
    max_iterations=100,
    tolerance=1.0e-8,
    dispersion_prior_df=10.0,
    ridge=1.0e-6,
    fdr_threshold=0.1,
    detection_filter=0.9,
  ) catch {
    _ => abort("failed to create muscat advanced configuration")
  }
}

///|
fn muscat_demo_result(
  result : @bio.MuscatAdvancedResults,
  genes : Array[String],
  clusters : Array[String],
  contrast : String,
) -> Unit {
  println("\n" + result.summary())
  for cluster in clusters {
    println("  cluster " + cluster)
    for gene in genes {
      match result.get(gene, cluster, contrast) {
        Some(value) =>
          println(
            "    " +
            gene +
            ": log2FC=" +
            value.log_fc.to_string() +
            ", Wald=" +
            value.statistic.to_string() +
            ", p=" +
            value.p_value.to_string() +
            ", local FDR=" +
            value.local_fdr.to_string() +
            ", global FDR=" +
            value.global_fdr.to_string(),
          )
        None => ()
      }
    }
  }
}

///|
fn muscat_demo_sce(data : @bio.MuscatAdvancedData) -> @bio.SingleCellExperiment {
  let experiment = @bio.SingleCellExperiment::new(
    data.counts,
    data.gene_names,
    data.cell_names,
  )
  experiment.col_data["sample"] = data.sample_ids.copy()
  experiment.col_data["cluster"] = data.cluster_ids.copy()
  experiment.col_data["group"] = data.group_ids.copy()
  experiment.row_data["symbol"] = data.gene_names.copy()
  experiment.metadata["source"] = "muscat advanced demo"
  experiment
}

///|
fn main {
  println("=== Bioconductor muscat 1.27.4 Advanced Demo ===")
  let data = @bio.muscat_advanced_example() catch {
    _ => abort("failed to construct muscat example data")
  }
  let config = muscat_demo_config()

  println("\n1. Strict gene x cell data contract")
  println(
    "  " +
    data.gene_names.length().to_string() +
    " genes, " +
    data.cell_names.length().to_string() +
    " cells, " +
    data.sample_names.length().to_string() +
    " samples, " +
    data.cluster_names.length().to_string() +
    " clusters",
  )

  let sums = @bio.muscat_aggregate_advanced(
    data,
    aggregation=@bio.muscat_sum_counts(),
  ) catch {
    _ => abort("sum-count aggregation failed")
  }
  let detections = @bio.muscat_aggregate_advanced(
    data,
    aggregation=@bio.muscat_number_detected(),
  ) catch {
    _ => abort("number-detected aggregation failed")
  }
  println("\n2. Cluster-sample pseudobulk aggregation")
  for cluster in 0.. abort("group design construction failed")
  }
  let contrasts = @bio.muscat_default_contrasts_advanced(design) catch {
    _ => abort("default contrast construction failed")
  }
  let contrast = contrasts[0].name
  println("\n3. Replicated design and contrast")
  println("  coefficients: " + design.coefficient_names.join(", "))
  println("  contrast: " + contrast)

  let ds = @bio.muscat_pbds_advanced(sums, design, contrasts, config~) catch {
    _ => abort("differential-state model failed")
  }
  let dd = @bio.muscat_pbdd_advanced(detections, design, contrasts, config~) catch {
    _ => abort("differential-detection model failed")
  }
  muscat_demo_result(ds, data.gene_names, data.cluster_names, contrast)
  muscat_demo_result(dd, data.gene_names, data.cluster_names, contrast)

  let stagewise = @bio.muscat_stagewise_ds_dd(ds, dd, alpha=0.1) catch {
    _ => abort("stagewise DS/DD testing failed")
  }
  println("\n4. Harmonic-mean screening and two-stage confirmation")
  for result in stagewise.results {
    if result.classification != "none" {
      println(
        "  " +
        result.cluster_name +
        "/" +
        result.gene_name +
        ": " +
        result.classification +
        ", screen FDR=" +
        result.screen_fdr.to_string(),
      )
    }
  }

  let sce_output = @bio.muscat_advanced_sce(
    muscat_demo_sce(data),
    "sample",
    "cluster",
    "group",
    reference="ctrl",
    output_prefix="muscat",
    config~,
  ) catch {
    _ => abort("SingleCellExperiment integration failed")
  }
  println("\n5. Immutable SingleCellExperiment write-back")
  println(
    "  metadata version: " + sce_output.experiment.metadata["muscat.version"],
  )
  println(
    "  row annotations: muscat.A.dsLogFC, muscat.A.dsFdr, " +
    "muscat.A.ddLogFC, muscat.A.ddFdr, muscat.A.stageClass",
  )
}