///|
/// A human-readable remediation hint for validation errors.
pub(all) struct FixSuggestion {
  row : Int
  column : String
  code : String
  suggestion : String
} derive(Eq)

///|
fn suggestion_for_error(err : ValidationError) -> String {
  match err.code {
    "required" => "fill a non-empty value"
    "type" => "convert the value to the expected type or relax the schema"
    "missing_column" => "add the required column to the header row"
    "extra_column" => "remove the column or validate with an open schema"
    _ => "inspect the source data near this location"
  }
}

///|
/// Convert validation errors into deterministic repair suggestions.
pub fn suggest_fixes(report : ValidationReport) -> Array[FixSuggestion] {
  let suggestions : Array[FixSuggestion] = []
  for err in report.errors {
    suggestions.push({
      row: err.row,
      column: err.column,
      code: err.code,
      suggestion: suggestion_for_error(err),
    })
  }
  suggestions
}

///|
fn write_validation_markdown_row(
  out : StringBuilder,
  row : String,
  column : String,
  code : String,
  message : String,
) -> Unit {
  out.write_string("| ")
  out.write_string(markdown_escape_cell(row))
  out.write_string(" | ")
  out.write_string(markdown_escape_cell(column))
  out.write_string(" | ")
  out.write_string(markdown_escape_cell(code))
  out.write_string(" | ")
  out.write_string(markdown_escape_cell(message))
  out.write_string(" |\n")
}

///|
/// Render validation errors as a Markdown table.
pub fn ValidationReport::to_markdown(self : ValidationReport) -> String {
  let out = StringBuilder::StringBuilder()
  write_validation_markdown_row(out, "row", "column", "code", "message")
  write_validation_markdown_row(out, "---", "---", "---", "---")
  for err in self.errors {
    write_validation_markdown_row(
      out,
      err.row.to_string(),
      err.column,
      err.code,
      err.message,
    )
  }
  out.to_string()
}