///|
/// Ask whether all the named chemical steps could have zero net transfer.
/// This is a joint hypothesis: ruling it out requires at least one of them,
/// not every named step, to be nonzero within the declared model.
pub(all) struct ZeroTransferClaim {
  study : AliquotStudy
  zero_steps : Array[String]
} derive(Debug, ToJson, FromJson)

///|
pub extend ZeroTransferClaim with @debug.Debug::{to_repr}

///|
pub extend ZeroTransferClaim with @json.ToJson::{to_json}

///|
pub extend ZeroTransferClaim with @json.FromJson::{from_json}

///|
pub(all) struct ClaimAssessment {
  status : String
  zero_steps : Array[String]
  base : AliquotAssessment
  assumed_zero : AliquotAssessment?
  diagnostics : Array[String]
} derive(Debug, ToJson)

///|
pub extend ClaimAssessment with @debug.Debug::{to_repr}

///|
pub extend ClaimAssessment with @json.ToJson::{to_json}

///|
/// Challenge a specific sample-handling explanation. An inconsistent baseline
/// never proves that a process occurred; a possible history never proves it did.
pub fn challenge_zero_transfer(
  claim : ZeroTransferClaim,
) -> Result[ClaimAssessment, String] {
  let compiled = match compile_study(claim.study) {
    Ok(c) => c
    Err(error) => return Err(error)
  }
  if claim.zero_steps.length() == 0 {
    return Err(
      "name at least one chemical step whose net transfer is claimed zero",
    )
  }
  let names : Array[String] = []
  let rows = compiled.rows.copy()
  for id in claim.zero_steps {
    if names.contains(id) {
      return Err("zero-transfer step ids must be unique")
    }
    names.push(id)
    let steps = claim.study.steps.filter(fn(s) { s.id == id })
    if steps.length() != 1 ||
      !["co2", "calcite", "hcl", "naoh"].contains(steps[0].kind) {
      return Err(
        "zero-transfer claims require existing co2/calcite/hcl/naoh steps",
      )
    }
    let dose = input_form(
      "step:" + id,
      steps[0].amount_nmol,
      compiled.parameters,
    )
    rows.push(inequality("claim:zero:" + id + ":upper", dose, r(0)))
    rows.push(
      inequality("claim:zero:" + id + ":lower", scale(dose, r(-1)), r(0)),
    )
  }
  let base = assess_compiled(compiled)
  if base.status != "Compatible" {
    return Ok({
      status: if base.status == "Conflict" {
        "BaseConflict"
      } else {
        "ResourceLimit"
      },
      zero_steps: names,
      base,
      assumed_zero: None,
      diagnostics: [
        "The baseline has no established compatible history. Do not attribute its disagreement to a named process.",
      ],
    })
  }
  let tested = assess_compiled({ ..compiled, rows, })
  let (status, diagnostic) = match tested.status {
    "Compatible" =>
      (
        "ZeroTransferPossible", "One shared history satisfies every assay with ALL named transfers zero. This does not establish that the real transfers were zero.",
      )
    "Conflict" =>
      (
        "ZeroTransferRuledOut", "The baseline fits, but ALL named transfers being zero is impossible. At least one named step must be nonzero within this declared model; this does not identify every step or establish physical causation.",
      )
    _ => ("ResourceLimit", "The zero-transfer hypothesis remains undecided.")
  }
  Ok({
    status,
    zero_steps: names,
    base,
    assumed_zero: Some(tested),
    diagnostics: [
      diagnostic, "Conditional on exact declared volumes, complete counterions, the listed process alternatives and inclusive assay bounds. Net zero does not mean no forward/reverse reaction.",
    ],
  })
}