///|
/// Combined comparison result for two JSON documents.
pub(all) struct ComparisonReport {
  valid : Bool
  canonical_equal : Bool
  left_digest : String?
  right_digest : String?
  differences : Array[JsonDiff]
  error : String?
}

///|
pub fn compare_documents(left : String, right : String) -> ComparisonReport {
  let left_value = match parse(left) {
    Ok(value) => value
    Err(error) =>
      return {
        valid: false,
        canonical_equal: false,
        left_digest: None,
        right_digest: None,
        differences: [],
        error: Some("left document: " + error.message()),
      }
  }
  let right_value = match parse(right) {
    Ok(value) => value
    Err(error) =>
      return {
        valid: false,
        canonical_equal: false,
        left_digest: None,
        right_digest: None,
        differences: [],
        error: Some("right document: " + error.message()),
      }
  }
  let left_digest = match canonical_digest(left_value) {
    Ok(digest) => digest
    Err(error) =>
      return {
        valid: false,
        canonical_equal: false,
        left_digest: None,
        right_digest: None,
        differences: [],
        error: Some("left document: " + error.message()),
      }
  }
  let right_digest = match canonical_digest(right_value) {
    Ok(digest) => digest
    Err(error) =>
      return {
        valid: false,
        canonical_equal: false,
        left_digest: Some(left_digest),
        right_digest: None,
        differences: [],
        error: Some("right document: " + error.message()),
      }
  }
  let differences = diff_json(left_value, right_value)
  {
    valid: true,
    canonical_equal: left_digest == right_digest,
    left_digest: Some(left_digest),
    right_digest: Some(right_digest),
    differences,
    error: None,
  }
}

///|
pub fn ComparisonReport::to_json(self : ComparisonReport) -> String {
  let left = match self.left_digest {
    Some(value) => canonical_string(value)
    None => "null"
  }
  let right = match self.right_digest {
    Some(value) => canonical_string(value)
    None => "null"
  }
  let error = match self.error {
    Some(value) => canonical_string(value)
    None => "null"
  }
  let mut differences = "["
  let mut first = true
  for difference in self.differences {
    if !first {
      differences = differences + ","
    }
    first = false
    differences = differences +
      "{\"path\":" +
      canonical_string(difference.path) +
      ",\"kind\":" +
      canonical_string(difference.kind.to_string()) +
      "}"
  }
  "{\"valid\":" +
  (if self.valid { "true" } else { "false" }) +
  ",\"canonical_equal\":" +
  (if self.canonical_equal { "true" } else { "false" }) +
  ",\"left_digest\":" +
  left +
  ",\"right_digest\":" +
  right +
  ",\"differences\":" +
  differences +
  "],\"error\":" +
  error +
  "}"
}