///|
/// Structural counts for an exposition document.
pub(all) struct MetricsSummary {
  family_count : Int
  sample_count : Int
  label_count : Int
  exemplar_count : Int
  counter_count : Int
  gauge_count : Int
  histogram_count : Int
  gauge_histogram_count : Int
  summary_count : Int
  info_count : Int
  state_set_count : Int
  unknown_count : Int
} derive(Eq)

///|
/// Count families, samples, labels, exemplars, and family types.
pub fn summarize(document : Document) -> MetricsSummary {
  let mut sample_count = 0
  let mut label_count = 0
  let mut exemplar_count = 0
  let mut counter_count = 0
  let mut gauge_count = 0
  let mut histogram_count = 0
  let mut gauge_histogram_count = 0
  let mut summary_count = 0
  let mut info_count = 0
  let mut state_set_count = 0
  let mut unknown_count = 0
  for family in document.families {
    match family.metric_type {
      Counter => counter_count += 1
      Gauge => gauge_count += 1
      Histogram => histogram_count += 1
      GaugeHistogram => gauge_histogram_count += 1
      Summary => summary_count += 1
      Info => info_count += 1
      StateSet => state_set_count += 1
      Unknown => unknown_count += 1
    }
    sample_count += family.samples.length()
    for sample in family.samples {
      label_count += sample.labels.length()
      match sample.exemplar {
        Some(exemplar) => {
          exemplar_count += 1
          label_count += exemplar.labels.length()
        }
        None => ()
      }
    }
  }
  {
    family_count: document.families.length(),
    sample_count,
    label_count,
    exemplar_count,
    counter_count,
    gauge_count,
    histogram_count,
    gauge_histogram_count,
    summary_count,
    info_count,
    state_set_count,
    unknown_count,
  }
}

///|
/// Return structural counts for this document.
pub fn Document::summary(self : Document) -> MetricsSummary {
  summarize(self)
}

///|
/// Render summary counts on one stable machine-readable line.
pub fn MetricsSummary::to_text(self : MetricsSummary) -> String {
  "families=\{self.family_count} samples=\{self.sample_count} labels=\{self.label_count} exemplars=\{self.exemplar_count} counter=\{self.counter_count} gauge=\{self.gauge_count} histogram=\{self.histogram_count} gaugehistogram=\{self.gauge_histogram_count} summary=\{self.summary_count} info=\{self.info_count} stateset=\{self.state_set_count} unknown=\{self.unknown_count}"
}

///|
fn severity_keyword(severity : Severity) -> String {
  match severity {
    Error => "error"
    Warning => "warning"
  }
}

///|
/// Render one validation issue without relying on debug formatting.
pub fn ValidationIssue::to_text(self : ValidationIssue) -> String {
  let family = if self.family == "" { "" } else { self.family }
  let sample = match self.sample {
    Some(name) => " sample=\{name}"
    None => ""
  }
  "\{severity_keyword(self.severity)} [\{self.code}] family=\{family}\{sample}: \{self.message}"
}

///|
/// Render all issues followed by an error/warning total.
pub fn ValidationReport::to_text(self : ValidationReport) -> String {
  let out = StringBuilder()
  for issue in self.issues {
    out.write_string(issue.to_text())
    out.write_char('\n')
  }
  out.write_string("errors=\{self.error_count} warnings=\{self.warning_count}")
  out.to_string()
}