///|
pub struct Coverage {
  total : Int
  allowed : Int
  blocked : Int
  unmatched : Int
} derive(Debug, Eq)

///|
pub fn coverage(
  text : String,
  agent : String,
  paths : Array[String],
) -> Coverage {
  let robots = parse(text)
  let mut allowed = 0
  let mut blocked = 0
  let mut unmatched = 0
  for path in paths {
    let decision = decide(robots, agent, path)
    if decision.matched_kind == "none" {
      unmatched += 1
    }
    if decision.allowed {
      allowed += 1
    } else {
      blocked += 1
    }
  }
  { total: paths.length(), allowed, blocked, unmatched }
}

///|
pub fn coverage_line(c : Coverage) -> String {
  "total=" +
  c.total.to_string() +
  " allowed=" +
  c.allowed.to_string() +
  " blocked=" +
  c.blocked.to_string() +
  " unmatched=" +
  c.unmatched.to_string()
}

///|
pub fn coverage_report(
  text : String,
  agent : String,
  paths : Array[String],
) -> String {
  coverage_line(coverage(text, agent, paths))
}

///|
pub fn sample_paths_from_rules(robots : Robots) -> Array[String] {
  let paths : Array[String] = ["/"]
  for group in robots.groups {
    for rule in group.rules {
      if rule.pattern != "" && !contains_string(paths, rule.pattern) {
        paths.push(rule.pattern)
      }
      let child = sample_child_path(rule.pattern)
      if child != "" && !contains_string(paths, child) {
        paths.push(child)
      }
    }
  }
  sort_paths_by_depth(paths)
}

///|
pub fn sample_child_path(pattern : String) -> String {
  if pattern == "" {
    ""
  } else if pattern.has_suffix("/") {
    pattern + "sample"
  } else if pattern.find("*") is Some(_) {
    text_replace(pattern, "*", "sample")
  } else {
    pattern + "/sample"
  }
}

///|
pub fn auto_coverage_report(text : String, agent : String) -> String {
  let paths = sample_paths_from_rules(parse(text))
  coverage_report(text, agent, paths)
}