///|
fn finding_severity_text(severity : FindingSeverity) -> String {
  match severity {
    FindingInfo => "info"
    FindingWarning => "warning"
    FindingError => "error"
  }
}

///|
fn change_kind_text(kind : RecordChangeKind) -> String {
  match kind {
    RecordAdded => "added"
    RecordRemoved => "removed"
    RecordTtlChanged => "ttl_changed"
    RecordDataChanged => "data_changed"
  }
}

///|
fn optional_ttl_json(value : Ttl?) -> Json {
  match value {
    Some(ttl) => Json::string(ttl.seconds().to_string())
    None => Json::null()
  }
}

///|
fn optional_record_text(value : ZoneRecord?) -> String {
  match value {
    Some(record) => record.to_text()
    None => "-"
  }
}

///|
fn optional_record_json(value : ZoneRecord?) -> Json {
  match value {
    Some(record) =>
      Json::object({
        "owner": Json::string(record.owner().to_text()),
        "ttl_seconds": Json::string(record.ttl().seconds().to_string()),
        "class": Json::string(record.record_class()),
        "record_type": Json::string(record.record_type()),
        "data": Json::string(render_record_data(record.data())),
        "canonical_text": Json::string(record.to_text()),
      })
    None => Json::null()
  }
}

///|
pub fn render_statistics_text(statistics : ZoneStatistics) -> String {
  let mut result = "DNS zone statistics" +
    "\nrecords: " +
    statistics.record_count().to_string() +
    "\nowners: " +
    statistics.owner_count().to_string() +
    "\napex records: " +
    statistics.apex_record_count().to_string() +
    "\ndelegation owners: " +
    statistics.delegation_owner_count().to_string() +
    "\nexplicit TTL records: " +
    statistics.explicit_ttl_count().to_string()
  match statistics.minimum_ttl() {
    Some(value) =>
      result = result + "\nminimum TTL seconds: " + value.seconds().to_string()
    None => result = result + "\nminimum TTL seconds: -"
  }
  match statistics.maximum_ttl() {
    Some(value) =>
      result = result + "\nmaximum TTL seconds: " + value.seconds().to_string()
    None => result = result + "\nmaximum TTL seconds: -"
  }
  match statistics.average_ttl_seconds() {
    Some(value) =>
      result = result + "\naverage TTL seconds: " + value.to_string()
    None => result = result + "\naverage TTL seconds: -"
  }
  result = result + "\nrecord types:"
  for item in statistics.type_counts() {
    result = result +
      "\n  " +
      item.record_type() +
      ": " +
      item.count().to_string()
  }
  result
}

///|
pub fn render_statistics_json(statistics : ZoneStatistics) -> String {
  let type_counts : Array[Json] = []
  for item in statistics.type_counts() {
    type_counts.push(
      Json::object({
        "record_type": Json::string(item.record_type()),
        "count": Json::number(item.count().to_double()),
      }),
    )
  }
  Json::object({
    "type": Json::string("dns_zone_statistics"),
    "record_count": Json::number(statistics.record_count().to_double()),
    "owner_count": Json::number(statistics.owner_count().to_double()),
    "apex_record_count": Json::number(
      statistics.apex_record_count().to_double(),
    ),
    "delegation_owner_count": Json::number(
      statistics.delegation_owner_count().to_double(),
    ),
    "explicit_ttl_count": Json::number(
      statistics.explicit_ttl_count().to_double(),
    ),
    "minimum_ttl_seconds": optional_ttl_json(statistics.minimum_ttl()),
    "maximum_ttl_seconds": optional_ttl_json(statistics.maximum_ttl()),
    "average_ttl_seconds": match statistics.average_ttl_seconds() {
      Some(value) => Json::string(value.to_string())
      None => Json::null()
    },
    "record_types": Json::array(type_counts),
  }).stringify()
}

///|
pub fn render_validation_text(validation : ZoneValidation) -> String {
  let mut result = "DNS zone validation" +
    "\nrecords: " +
    validation.record_count().to_string() +
    "\naccepted: " +
    validation.accepted().to_string() +
    "\nerrors: " +
    validation.error_count().to_string() +
    "\nwarnings: " +
    validation.warning_count().to_string()
  for finding in validation.findings() {
    result = result +
      "\n[" +
      finding_severity_text(finding.severity()) +
      "] " +
      finding.code() +
      ": " +
      finding.message()
    match finding.owner() {
      Some(value) => result = result + " (owner " + value.to_text() + ")"
      None => ()
    }
  }
  result
}

///|
pub fn render_validation_json(validation : ZoneValidation) -> String {
  let findings : Array[Json] = []
  for finding in validation.findings() {
    findings.push(
      Json::object({
        "code": Json::string(finding.code()),
        "severity": Json::string(finding_severity_text(finding.severity())),
        "message": Json::string(finding.message()),
        "owner": match finding.owner() {
          Some(value) => Json::string(value.to_text())
          None => Json::null()
        },
      }),
    )
  }
  Json::object({
    "type": Json::string("dns_zone_validation"),
    "record_count": Json::number(validation.record_count().to_double()),
    "accepted": Json::boolean(validation.accepted()),
    "error_count": Json::number(validation.error_count().to_double()),
    "warning_count": Json::number(validation.warning_count().to_double()),
    "findings": Json::array(findings),
  }).stringify()
}

///|
pub fn render_comparison_text(comparison : ZoneComparison) -> String {
  let mut result = "DNS zone comparison" +
    "\nidentical: " +
    comparison.identical().to_string() +
    "\nadded: " +
    comparison.added_count().to_string() +
    "\nremoved: " +
    comparison.removed_count().to_string() +
    "\nTTL changed: " +
    comparison.ttl_changed_count().to_string() +
    "\ndata changed: " +
    comparison.data_changed_count().to_string()
  for change in comparison.changes() {
    result = result +
      "\n[" +
      change_kind_text(change.kind()) +
      "] left=" +
      optional_record_text(change.left()) +
      " | right=" +
      optional_record_text(change.right())
  }
  result
}

///|
pub fn render_comparison_json(comparison : ZoneComparison) -> String {
  let changes : Array[Json] = []
  for change in comparison.changes() {
    changes.push(
      Json::object({
        "kind": Json::string(change_kind_text(change.kind())),
        "left": optional_record_json(change.left()),
        "right": optional_record_json(change.right()),
      }),
    )
  }
  Json::object({
    "type": Json::string("dns_zone_comparison"),
    "identical": Json::boolean(comparison.identical()),
    "added_count": Json::number(comparison.added_count().to_double()),
    "removed_count": Json::number(comparison.removed_count().to_double()),
    "ttl_changed_count": Json::number(
      comparison.ttl_changed_count().to_double(),
    ),
    "data_changed_count": Json::number(
      comparison.data_changed_count().to_double(),
    ),
    "changes": Json::array(changes),
  }).stringify()
}