///|
pub fn analyze_readme(readme : String) -> ReadmeMetrics {
  let lines = split_lines(readme)
  let sections = collect_sections(lines)
  let mut heading_count = 0
  let mut code_block_count = 0
  let mut command_count = 0
  let mut link_count = 0
  let mut bullet_count = 0
  let mut in_fence = false
  let mut index = 0
  while index < lines.length() {
    let line = lines[index]
    if is_heading_line(line) {
      heading_count += 1
    }
    if is_fence_line(line) {
      code_block_count += 1
      in_fence = !in_fence
    } else if in_fence && is_command_line(line) {
      command_count += 1
    }
    if is_markdown_link_line(line) {
      link_count += 1
    }
    if is_bullet_line(line) || is_numbered_line(line) {
      bullet_count += 1
    }
    index += 1
  }
  readme_metrics(
    lines.length(),
    heading_count,
    code_block_count / 2,
    command_count,
    link_count,
    bullet_count,
    readme_has_installation(readme),
    readme_has_usage(readme),
    readme_has_example(readme),
    readme_has_validation(readme),
    readme_has_license(readme),
    readme_has_support_scope(readme),
    readme_has_unsupported_scope(readme),
    sections,
  )
}

///|
pub fn ReadmeMetrics::coverage_score(self : ReadmeMetrics) -> Int {
  let mut score = 0
  if self.has_installation {
    score += 15
  }
  if self.has_usage {
    score += 15
  }
  if self.has_example {
    score += 15
  }
  if self.has_validation {
    score += 15
  }
  if self.has_license {
    score += 10
  }
  if self.has_support_scope {
    score += 10
  }
  if self.has_unsupported_scope {
    score += 10
  }
  if self.code_block_count > 0 {
    score += 5
  }
  if self.command_count > 0 {
    score += 5
  }
  score
}

///|
pub fn ReadmeMetrics::is_complete(self : ReadmeMetrics) -> Bool {
  self.coverage_score() >= 90 &&
  self.heading_count >= 6 &&
  self.command_count >= 3
}

///|
pub fn ReadmeMetrics::summary(self : ReadmeMetrics) -> String {
  "readme lines=\{self.line_count}, headings=\{self.heading_count}, commands=\{self.command_count}, coverage=\{self.coverage_score()}"
}

///|
pub fn ReadmeMetrics::missing_topics(self : ReadmeMetrics) -> Array[String] {
  let missing : Array[String] = []
  if !self.has_installation {
    missing.push("installation")
  }
  if !self.has_usage {
    missing.push("usage")
  }
  if !self.has_example {
    missing.push("example")
  }
  if !self.has_validation {
    missing.push("validation")
  }
  if !self.has_license {
    missing.push("license")
  }
  if !self.has_support_scope {
    missing.push("support scope")
  }
  if !self.has_unsupported_scope {
    missing.push("unsupported scope")
  }
  missing
}

///|
pub fn ReadmeMetrics::to_markdown(self : ReadmeMetrics) -> String {
  let mut out = "## README Metrics\n\n"
  out = out + "- Lines: " + self.line_count.to_string() + "\n"
  out = out + "- Headings: " + self.heading_count.to_string() + "\n"
  out = out + "- Code blocks: " + self.code_block_count.to_string() + "\n"
  out = out + "- Commands: " + self.command_count.to_string() + "\n"
  out = out + "- Links: " + self.link_count.to_string() + "\n"
  out = out + "- Bullets: " + self.bullet_count.to_string() + "\n"
  out = out + "- Coverage score: " + self.coverage_score().to_string() + "\n\n"
  out = out + "### Sections\n\n"
  let mut index = 0
  while index < self.sections.length() {
    let section = self.sections[index]
    out = out + "- L" + section.line.to_string()
    out = out + " H" + section.level.to_string()
    out = out + " " + section.title
    out = out + " (" + section.body_lines.to_string() + " body lines)\n"
    index += 1
  }
  out
}

///|
fn collect_sections(lines : Array[String]) -> Array[SectionInfo] {
  let sections : Array[SectionInfo] = []
  let mut index = 0
  while index < lines.length() {
    let line = lines[index]
    if is_heading_line(line) {
      let level = heading_level(line)
      let title = heading_text(line)
      let body_lines = count_section_body(lines, index, level)
      sections.push(section_info(title, level, index + 1, body_lines))
    }
    index += 1
  }
  sections
}

///|
fn count_section_body(lines : Array[String], start : Int, level : Int) -> Int {
  let mut count = 0
  let mut index = start + 1
  while index < lines.length() {
    if is_heading_line(lines[index]) && heading_level(lines[index]) <= level {
      return count
    }
    if trim_ascii(lines[index]).length() > 0 {
      count += 1
    }
    index += 1
  }
  count
}

///|
fn readme_has_installation(readme : String) -> Bool {
  contains(readme, "安装") ||
  contains_word(readme, "installation") ||
  contains_word(readme, "install") ||
  contains_word(readme, "moon add")
}

///|
fn readme_has_usage(readme : String) -> Bool {
  contains(readme, "使用") ||
  contains(readme, "用法") ||
  contains_word(readme, "usage") ||
  contains_word(readme, "api")
}

///|
fn readme_has_example(readme : String) -> Bool {
  contains(readme, "示例") ||
  contains_word(readme, "example") ||
  contains_word(readme, "moon run")
}

///|
fn readme_has_validation(readme : String) -> Bool {
  contains(readme, "验收") ||
  contains(readme, "测试") ||
  contains_word(readme, "validation") ||
  contains_word(readme, "moon test") ||
  contains_word(readme, "moon check")
}

///|
fn readme_has_license(readme : String) -> Bool {
  contains(readme, "许可证") ||
  contains_word(readme, "license") ||
  contains_word(readme, "MIT") ||
  contains_word(readme, "Apache")
}

///|
fn readme_has_support_scope(readme : String) -> Bool {
  contains(readme, "支持范围") ||
  contains_word(readme, "support scope") ||
  contains_word(readme, "supported")
}

///|
fn readme_has_unsupported_scope(readme : String) -> Bool {
  contains(readme, "暂不支持") ||
  contains(readme, "不支持") ||
  contains_word(readme, "unsupported") ||
  contains_word(readme, "not support")
}

///|
pub fn readme_topic_table(metrics : ReadmeMetrics) -> String {
  let mut out = "| Topic | Present |\n| --- | --- |\n"
  out = out + topic_row("installation", metrics.has_installation)
  out = out + topic_row("usage", metrics.has_usage)
  out = out + topic_row("example", metrics.has_example)
  out = out + topic_row("validation", metrics.has_validation)
  out = out + topic_row("license", metrics.has_license)
  out = out + topic_row("support scope", metrics.has_support_scope)
  out = out + topic_row("unsupported scope", metrics.has_unsupported_scope)
  out
}

///|
fn topic_row(name : String, present : Bool) -> String {
  let status = if present { "yes" } else { "no" }
  "| " + name + " | " + status + " |\n"
}

///|
pub fn longest_section_title(metrics : ReadmeMetrics) -> String {
  let mut best = ""
  let mut best_size = -1
  let mut index = 0
  while index < metrics.sections.length() {
    let section = metrics.sections[index]
    if section.body_lines > best_size {
      best = section.title
      best_size = section.body_lines
    }
    index += 1
  }
  best
}