///|
fn fixture_species(
  name : String,
  charge : Int32,
  stoich : Array[Double],
  terms : Array[ComponentTerm],
  base : Int32?,
  alkalinity : Double,
  source : String,
) -> SpeciesDef {
  let log_k = match thermo_record(builtin_thermodynamics(), source) {
    Some(record) => record.log_k
    None => 0.0 // All builtin identifiers are checked by validation and tests.
  }
  {
    name,
    charge,
    element_stoich: stoich,
    component_terms: terms,
    base_component: base,
    log_k,
    alkalinity,
    source,
  }
}

///|
fn term(component : Int32, coefficient : Double) -> ComponentTerm {
  { component, coefficient, }
}

///|
fn fixture_mineral(
  name : String,
  terms : Array[ComponentTerm],
  source : String,
) -> MineralDef {
  let log_ksp = match thermo_record(builtin_thermodynamics(), source) {
    Some(record) => record.log_k
    None => 0.0
  }
  { name, component_terms: terms, log_ksp, source, }
}

///|
fn fixture_problem(
  elements : Array[ElementDef],
  species : Array[SpeciesDef],
  minerals : Array[MineralDef],
  alkalinity : Double?,
) -> Problem {
  let thermodynamics = builtin_thermodynamics()
  {
    temperature_k: 298.15,
    volume_l: 1.0,
    elements,
    species,
    minerals,
    hydrogen_component: 0,
    fixed_p_h: None,
    alkalinity_mol_l: alkalinity,
    activity_model: "Davies",
    database_version: thermodynamics.version,
    thermodynamics,
  }
}

///|
/// Calcium-carbonate water, 2 mM Ca, 3 mM dissolved inorganic carbon,
/// 3 meq/L alkalinity and 1 mM chloride. The counterion closes charge.
pub fn demo_problem() -> Problem {
  fixture_problem(
    [
      { name: "Ca", total_mol_l: 0.002, },
      { name: "C", total_mol_l: 0.003, },
      { name: "Cl", total_mol_l: 0.001, },
    ],
    [
      fixture_species("H+", 1, [0, 0, 0], [], Some(0), -1, "identity"),
      fixture_species("Ca2+", 2, [1, 0, 0], [], Some(1), 0, "identity"),
      fixture_species("CO3--", -2, [0, 1, 0], [], Some(2), 2, "identity"),
      fixture_species("Cl-", -1, [0, 0, 1], [], Some(3), 0, "identity"),
      fixture_species(
        "HCO3-",
        -1,
        [0, 1, 0],
        [term(0, 1), term(2, 1)],
        None,
        1,
        "bicarbonate",
      ),
      fixture_species(
        "CO2(aq)",
        0,
        [0, 1, 0],
        [term(0, 2), term(2, 1)],
        None,
        0,
        "carbon-dioxide",
      ),
      fixture_species("OH-", -1, [0, 0, 0], [term(0, -1)], None, 1, "water"),
      fixture_species(
        "CaHCO3+",
        1,
        [1, 1, 0],
        [term(0, 1), term(1, 1), term(2, 1)],
        None,
        1,
        "calcium-bicarbonate",
      ),
      fixture_species(
        "CaCO3(aq)",
        0,
        [1, 1, 0],
        [term(1, 1), term(2, 1)],
        None,
        2,
        "calcium-carbonate",
      ),
      fixture_species(
        "CaOH+",
        1,
        [1, 0, 0],
        [term(1, 1), term(0, -1)],
        None,
        1,
        "calcium-hydrolysis",
      ),
    ],
    [fixture_mineral("Calcite", [term(1, 1), term(2, 1)], "calcite")],
    Some(0.003),
  )
}

///|
/// A closed, electroneutral 10 mM CaSO4 solution; includes bisulfate,
/// CaSO4(aq), CaHSO4+ and CaOH+, with gypsum saturation diagnostics.
pub fn sulfate_problem() -> Problem {
  fixture_problem(
    [{ name: "Ca", total_mol_l: 0.010, }, { name: "S", total_mol_l: 0.010, }],
    [
      fixture_species("H+", 1, [0, 0], [], Some(0), -1, "identity"),
      fixture_species("Ca2+", 2, [1, 0], [], Some(1), 0, "identity"),
      fixture_species("SO4--", -2, [0, 1], [], Some(2), 0, "identity"),
      fixture_species(
        "HSO4-",
        -1,
        [0, 1],
        [term(0, 1), term(2, 1)],
        None,
        -1,
        "bisulfate",
      ),
      fixture_species("OH-", -1, [0, 0], [term(0, -1)], None, 1, "water"),
      fixture_species(
        "CaSO4(aq)",
        0,
        [1, 1],
        [term(1, 1), term(2, 1)],
        None,
        0,
        "calcium-sulfate",
      ),
      fixture_species(
        "CaHSO4+",
        1,
        [1, 1],
        [term(0, 1), term(1, 1), term(2, 1)],
        None,
        -1,
        "calcium-bisulfate",
      ),
      fixture_species(
        "CaOH+",
        1,
        [1, 0],
        [term(1, 1), term(0, -1)],
        None,
        1,
        "calcium-hydrolysis",
      ),
    ],
    [fixture_mineral("Gypsum", [term(1, 1), term(2, 1)], "gypsum")],
    None,
  )
}

///|
/// Dilute zinc nitrate titrated with 19 micromol/L NaOH. All counterions
/// are explicit; pH follows charge balance. No carbonate or other ligands.
pub fn hydrolysis_problem() -> Problem {
  fixture_problem(
    [
      { name: "Zn", total_mol_l: 1.0e-5, },
      { name: "N", total_mol_l: 2.0e-5, },
      { name: "Na", total_mol_l: 1.9e-5, },
    ],
    [
      fixture_species("H+", 1, [0, 0, 0], [], Some(0), -1, "identity"),
      fixture_species("Zn2+", 2, [1, 0, 0], [], Some(1), 0, "identity"),
      fixture_species("NO3-", -1, [0, 1, 0], [], Some(2), 0, "identity"),
      fixture_species("Na+", 1, [0, 0, 1], [], Some(3), 0, "identity"),
      fixture_species("OH-", -1, [0, 0, 0], [term(0, -1)], None, 1, "water"),
      fixture_species(
        "ZnOH+",
        1,
        [1, 0, 0],
        [term(1, 1), term(0, -1)],
        None,
        1,
        "zinc-oh-1",
      ),
      fixture_species(
        "Zn(OH)2(aq)",
        0,
        [1, 0, 0],
        [term(1, 1), term(0, -2)],
        None,
        2,
        "zinc-oh-2",
      ),
      fixture_species(
        "Zn(OH)3-",
        -1,
        [1, 0, 0],
        [term(1, 1), term(0, -3)],
        None,
        3,
        "zinc-oh-3",
      ),
      fixture_species(
        "Zn(OH)4--",
        -2,
        [1, 0, 0],
        [term(1, 1), term(0, -4)],
        None,
        4,
        "zinc-oh-4",
      ),
    ],
    [
      fixture_mineral(
        "Zn(OH)2(e)",
        [term(1, 1), term(0, -2)],
        "zinc-hydroxide-solid",
      ),
    ],
    None,
  )
}

///|
pub fn pure_water_problem() -> Problem {
  fixture_problem(
    [],
    [
      fixture_species("H+", 1, [], [], Some(0), -1, "identity"),
      fixture_species("OH-", -1, [], [term(0, -1)], None, 1, "water"),
    ],
    [],
    None,
  )
}

///|
pub fn named_problem(name : String) -> Result[Problem, SolveError] {
  match name {
    "carbonate" => Ok(demo_problem())
    "sulfate" => Ok(sulfate_problem())
    "hydrolysis" => Ok(hydrolysis_problem())
    "water" => Ok(pure_water_problem())
    _ =>
      Err(
        InvalidInput("fixture must be carbonate, sulfate, hydrolysis or water"),
      )
  }
}