///|
/// Quality counters for dictionary ingestion pipelines.
pub(all) struct DictionaryQuality {
  entry_count : Int
  valid_count : Int
  invalid_count : Int
  duplicate_count : Int
  label_count : Int
  minimum_keyword_length : Int
  maximum_keyword_length : Int
  average_keyword_length : Int
} derive(Debug, Eq, ToJson)

///|
/// Inspect entries before compilation and return deterministic quality data.
///
/// Importers can use this report to reject malformed input without losing the
/// rest of the batch. Duplicate identities are counted after validation so a
/// malformed duplicate is still reported as malformed rather than hidden.
pub fn assess_dictionary(entries : ArrayView[Entry]) -> DictionaryQuality {
  let mut valid = 0
  let mut invalid = 0
  let mut duplicates = 0
  let mut total_length = 0
  let mut minimum = 0
  let mut maximum = 0
  let labels : Map[String, Bool] = Map([])
  let identities : Map[String, Bool] = Map([])
  for item in entries {
    let issues = validate_entry(item)
    if issues.length() == 0 {
      valid += 1
    } else {
      invalid += 1
    }
    let length = item.keyword.length()
    total_length += length
    if minimum == 0 || length < minimum {
      minimum = length
    }
    if length > maximum {
      maximum = length
    }
    labels[item.label] = true
    let identity = entry_key(item)
    if identities.contains(identity) {
      duplicates += 1
    } else {
      identities[identity] = true
    }
  }
  {
    entry_count: entries.length(),
    valid_count: valid,
    invalid_count: invalid,
    duplicate_count: duplicates,
    label_count: labels.length(),
    minimum_keyword_length: minimum,
    maximum_keyword_length: maximum,
    average_keyword_length: if entries.length() == 0 {
      0
    } else {
      total_length / entries.length()
    },
  }
}

///|
/// Return whether a dictionary is safe to compile without data loss.
pub fn dictionary_quality_is_usable(report : DictionaryQuality) -> Bool {
  report.entry_count > 0 &&
  report.invalid_count == 0 &&
  report.duplicate_count == 0
}

///|
/// Return the number of valid entries in a batch.
pub fn valid_entry_count(entries : ArrayView[Entry]) -> Int {
  assess_dictionary(entries).valid_count
}

///|
/// Return the number of repeated keyword/label identities in a batch.
pub fn duplicate_entry_count(entries : ArrayView[Entry]) -> Int {
  assess_dictionary(entries).duplicate_count
}

///|
/// Render a stable one-row report for import logs and CI artifacts.
pub fn dictionary_quality_tsv(report : DictionaryQuality) -> String {
  let header = "entries\tvalid\tinvalid\tduplicates\tlabels\tmin_length\tmax_length\taverage_length\n"
  let row = "\{report.entry_count}\t\{report.valid_count}\t\{report.invalid_count}\t\{report.duplicate_count}\t\{report.label_count}\t\{report.minimum_keyword_length}\t\{report.maximum_keyword_length}\t\{report.average_keyword_length}\n"
  header + row
}