///|
pub fn PantryPlan::summary(self : PantryPlan) -> String {
  "pantrycalc=" +
  self.verdict +
  " servings=" +
  self.servings.to_string() +
  " missing=" +
  self.missing_count.to_string() +
  " substitutions=" +
  self.substitution_count.to_string() +
  " score=" +
  plan_score(self).to_string()
}

///|
pub fn PantryPlan::to_markdown(self : PantryPlan) -> String {
  let out = StringBuilder()
  out.write_string("# PantryCalc Plan\n\n")
  out.write_string("- Recipe: " + self.recipe_title + "\n")
  out.write_string("- Verdict: " + self.verdict + "\n")
  out.write_string("- Servings: " + self.servings.to_string() + "\n")
  out.write_string("- Score: " + plan_score(self).to_string() + "/100\n")
  out.write_string("- Cost: " + money_text(self.total_cost_cents) + "\n")
  out.write_string("- Kcal: " + self.total_kcal.to_string() + "\n")
  out.write_string("- Protein: " + self.total_protein_g.to_string() + "g\n")
  out.write_string("- Missing items: " + self.missing_count.to_string() + "\n")
  out.write_string(
    "- Allergen hits: " + self.allergen_hit_count.to_string() + "\n\n",
  )
  out.write_string("## Shopping\n\n")
  if self.shopping.length() == 0 {
    out.write_string("- No shopping lines.\n")
  }
  for line in self.shopping {
    if line.buy.amount > 0 {
      out.write_string(
        "- " +
        line.name +
        ": buy " +
        quantity_text(line.buy) +
        " (need " +
        quantity_text(line.needed) +
        ", have " +
        quantity_text(line.available) +
        ", estimate " +
        money_text(line.estimated_cost_cents) +
        ")\n",
      )
    }
  }
  out.write_string("\n## Substitutions\n\n")
  if self.substitutions.length() == 0 {
    out.write_string("- No substitutions used.\n")
  }
  for sub in self.substitutions {
    out.write_string(
      "- " + sub.source + " -> " + sub.target.name + " (" + sub.reason + ")\n",
    )
  }
  out.write_string("\n## Warnings\n\n")
  if self.warnings.length() == 0 {
    out.write_string("- No warnings.\n")
  }
  for warning in self.warnings {
    out.write_string(
      "- [" +
      warning.severity +
      "] " +
      warning.code +
      ": " +
      warning.message +
      "\n",
    )
  }
  out.to_string()
}

///|
pub fn recipe_card(recipe : Recipe) -> String {
  let out = StringBuilder()
  out.write_string("# " + recipe.title + "\n\n")
  out.write_string("- Servings: " + recipe.servings.to_string() + "\n")
  out.write_string("- Cost: " + money_text(total_cost(recipe)) + "\n")
  let n = total_nutrition(recipe)
  out.write_string("- Kcal: " + n.kcal.to_string() + "\n")
  out.write_string("- Protein: " + n.protein_g.to_string() + "g\n\n")
  out.write_string("## Ingredients\n\n")
  for ingredient in recipe.ingredients {
    out.write_string(
      "- " + quantity_text(ingredient.quantity) + " " + ingredient.name,
    )
    if ingredient.allergens.length() > 0 {
      out.write_string(" allergens=" + join_text(ingredient.allergens))
    }
    out.write_string("\n")
  }
  out.write_string("\n## Steps\n\n")
  for i in 0.. String {
  let out = StringBuilder()
  out.write_string("name,needed,available,buy,cost\n")
  for line in lines {
    out.write_string(line.name + ",")
    out.write_string(quantity_text(line.needed) + ",")
    out.write_string(quantity_text(line.available) + ",")
    out.write_string(quantity_text(line.buy) + ",")
    out.write_string(money_text(line.estimated_cost_cents) + "\n")
  }
  out.to_string()
}