///|
pub(all) struct ReportSection {
  id : String
  title : String
  body : String
  priority : Int
} derive(Debug, Eq, ToJson)

///|
pub(all) struct ReportDocument {
  title : String
  sections : Array[ReportSection]
  format : String
} derive(Debug, Eq, ToJson)

///|
pub fn ReportSection::new(
  id~ : String,
  title~ : String,
  body~ : String,
  priority~ : Int,
) -> ReportSection {
  { id, title, body, priority }
}

///|
pub fn findings_section(analysis : Analysis) -> ReportSection {
  ReportSection::new(
    id="findings",
    title="Findings",
    body=analysis_markdown(analysis),
    priority=10,
  )
}

///|
pub fn summary_section(analysis : Analysis) -> ReportSection {
  let summary = summarize(analysis)
  let body = summary_csv(summary)
  ReportSection::new(id="summary", title="Summary", body~, priority=0)
}

///|
pub fn recommendation_section(analysis : Analysis) -> ReportSection {
  ReportSection::new(
    id="recommendations",
    title="Recommendations",
    body=recommendations_markdown(analysis),
    priority=20,
  )
}

///|
pub fn practice_section(analysis : Analysis) -> ReportSection {
  let plan = plan_for_analysis(analysis)
  ReportSection::new(
    id="practice",
    title="Practice plan",
    body=plan_markdown(plan),
    priority=30,
  )
}

///|
pub fn rhythm_section(meter : Meter, notes : Array[Note]) -> ReportSection {
  let summary = summarize_rhythm(meter, notes)
  let body = "notes=\{summary.notes}\nmeasures=\{summary.measures}\nsyncopations=\{summary.syncopations}\ndensity=\{rhythm_density(meter, notes)}"
  ReportSection::new(id="rhythm", title="Rhythm", body~, priority=15)
}

///|
pub fn contour_section(notes : Array[Note]) -> ReportSection {
  let summary = contour_summary(notes)
  let body = "notes=\{summary.notes}\nrising=\{summary.rising}\nfalling=\{summary.falling}\nleaps=\{summary.leaps}\nrange=\{summary.range}"
  ReportSection::new(id="contour", title="Melodic contour", body~, priority=16)
}

///|
pub fn harmony_section(
  lower : Array[Note],
  upper : Array[Note],
) -> ReportSection {
  let summary = harmony_summary(lower, upper)
  let body = "events=\{summary.events}\nperfect=\{summary.perfect}\nimperfect=\{summary.imperfect}\ndissonant=\{summary.dissonant}\ncrossings=\{summary.crossings}"
  ReportSection::new(id="harmony", title="Harmony", body~, priority=17)
}

///|
pub fn sections_for_analysis(
  analysis : Analysis,
  meter : Meter,
  cantus : Array[Note],
  counter : Array[Note],
) -> Array[ReportSection] {
  [
    summary_section(analysis),
    findings_section(analysis),
    recommendation_section(analysis),
    practice_section(analysis),
    rhythm_section(meter, counter),
    contour_section(counter),
    harmony_section(cantus, counter),
  ]
}

///|
pub fn report_document(
  title : String,
  analysis : Analysis,
  meter : Meter,
  cantus : Array[Note],
  counter : Array[Note],
) -> ReportDocument {
  {
    title,
    sections: sections_for_analysis(analysis, meter, cantus, counter),
    format: "markdown-sections-v1",
  }
}

///|
pub fn section_by_id(document : ReportDocument, id : String) -> ReportSection? {
  for section in document.sections {
    if section.id == id {
      return Some(section)
    }
  }
  None
}

///|
pub fn sections_by_priority(
  document : ReportDocument,
  minimum : Int,
) -> Array[ReportSection] {
  let result : Array[ReportSection] = []
  for section in document.sections {
    if section.priority >= minimum {
      result.push(section)
    }
  }
  result
}

///|
pub fn render_section(section : ReportSection) -> String {
  "## \{section.title}\n\n\{section.body}"
}

///|
pub fn render_document(document : ReportDocument) -> String {
  let lines : Array[String] = ["# \{document.title}", ""]
  for section in document.sections {
    lines.push(render_section(section))
    lines.push("")
  }
  lines.join("\n")
}

///|
pub fn report_section_count(document : ReportDocument) -> Int {
  document.sections.length()
}

///|
pub fn report_contains(document : ReportDocument, id : String) -> Bool {
  section_by_id(document, id) is Some(_)
}

///|
pub fn report_word_count(document : ReportDocument) -> Int {
  let mut total = 0
  for section in document.sections {
    total += section.body.split(" ").length()
  }
  total
}

///|
pub fn report_quality_score(
  document : ReportDocument,
  analysis : Analysis,
) -> Int {
  let base = summarize(analysis).score
  if report_section_count(document) >= 5 {
    base
  } else {
    base / 2
  }
}

///|
pub fn compact_report_document(document : ReportDocument) -> String {
  let lines : Array[String] = []
  for section in document.sections {
    lines.push("\{section.id}=\{section.priority}")
  }
  lines.join(";")
}

///|
pub fn report_section_ids(document : ReportDocument) -> Array[String] {
  let result : Array[String] = []
  for section in document.sections {
    result.push(section.id)
  }
  result
}

///|
pub fn append_report_section(
  document : ReportDocument,
  section : ReportSection,
) -> ReportDocument {
  let sections = document.sections.copy()
  sections.push(section)
  { ..document, sections, }
}

///|
pub fn report_with_manifest(
  document : ReportDocument,
  profile : RuleProfileConfig,
  meter : Meter,
) -> ReportDocument {
  let section = ReportSection::new(
    id="manifest",
    title="Manifest",
    body=manifest_text(profile, meter),
    priority=-1,
  )
  append_report_section(document, section)
}