///|
/// Fully prepared canonical document and its audit metadata.
pub(all) struct CanonicalDocument {
  canonical : String
  digest : String
  byte_length : Int
  stats : JsonStats
  issue_count : Int
}

///|
/// Parse, normalize, hash, and collect metrics once for repeated consumers.
pub fn prepare_document(input : String) -> Result[CanonicalDocument, String] {
  let value = match parse(input) {
    Ok(value) => value
    Err(error) => return Err(error.message())
  }
  inspect_value(value)
}

///|
/// Prepare a document while enforcing limits before collecting audit data.
pub fn prepare_document_with_limits(
  input : String,
  limits : DocumentLimits,
) -> Result[CanonicalDocument, String] {
  let value = match parse_with_limits(input, limits) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  inspect_value(value)
}

///|
fn inspect_value(value : JsonValue) -> Result[CanonicalDocument, String] {
  let stats = JsonStats::empty()
  collect_stats(value, 0, stats)
  let canonical = match canonicalize(value) {
    Ok(canonical) => canonical
    Err(error) => return Err(error.message())
  }
  let digest = match canonical_digest(value) {
    Ok(digest) => digest
    Err(error) => return Err(error.message())
  }
  let issue_count = canonical_issues(value).length()
  Ok({
    canonical,
    digest,
    byte_length: @utf8.encode(canonical).length(),
    stats,
    issue_count,
  })
}

///|
pub fn CanonicalDocument::to_json(self : CanonicalDocument) -> String {
  "{\"canonical\":" +
  canonical_string(self.canonical) +
  ",\"digest\":" +
  canonical_string(self.digest) +
  ",\"byte_length\":" +
  self.byte_length.to_string() +
  ",\"issue_count\":" +
  self.issue_count.to_string() +
  ",\"stats\":" +
  self.stats.to_json() +
  "}"
}

///|
pub fn CanonicalDocument::summary(self : CanonicalDocument) -> String {
  "bytes=" +
  self.byte_length.to_string() +
  ",sha256=" +
  self.digest +
  ",values=" +
  self.stats.value_count.to_string() +
  ",issues=" +
  self.issue_count.to_string()
}