///|
/// Severity level for a project health diagnostic.
pub(all) enum Severity {
  Error
  Warn
  Info
} derive(Debug, Eq)

///|
pub fn Severity::label(self : Severity) -> String {
  match self {
    Error => "ERROR"
    Warn => "WARN"
    Info => "INFO"
  }
}

///|
/// Quality area that owns a release-readiness rule.
pub(all) enum RuleCategory {
  Manifest
  Package
  Documentation
  Ci
  Release
} derive(Debug, Eq)

///|
pub fn RuleCategory::label(self : RuleCategory) -> String {
  match self {
    Manifest => "manifest"
    Package => "package"
    Documentation => "documentation"
    Ci => "ci"
    Release => "release"
  }
}

///|
/// Source location attached to a diagnostic when a parser can identify it.
pub(all) struct SourceLocation {
  path : String
  line : Int
  column : Int
} derive(Debug, Eq)

///|
pub fn SourceLocation::new(
  path : String,
  line : Int,
  column : Int,
) -> SourceLocation {
  { path, line, column }
}

///|
/// Stable metadata for an explainable Moon Doctor rule.
pub(all) struct RuleDefinition {
  id : String
  category : RuleCategory
  default_severity : Severity
  title : String
  description : String
  remediation : String
  profiles : Array[String]
} derive(Debug, Eq)

///|
pub fn RuleDefinition::new(
  id : String,
  category : RuleCategory,
  default_severity : Severity,
  title : String,
  description : String,
  remediation : String,
  profiles : Array[String],
) -> RuleDefinition {
  { id, category, default_severity, title, description, remediation, profiles }
}

///|
/// One finding produced by Moon Doctor.
pub(all) struct Diagnostic {
  severity : Severity
  code : String
  message : String
  detail : String
  location : SourceLocation?
  remediation : Array[String]
} derive(Debug, Eq)

///|
pub fn Diagnostic::new(
  severity : Severity,
  code : String,
  message : String,
  detail : String,
) -> Diagnostic {
  { severity, code, message, detail, location: None, remediation: [] }
}

///|
pub fn Diagnostic::at(
  severity : Severity,
  code : String,
  message : String,
  detail : String,
  location : SourceLocation,
  remediation : Array[String],
) -> Diagnostic {
  { severity, code, message, detail, location: Some(location), remediation }
}

///|
/// Final score summary for a diagnostic run.
pub(all) struct Score {
  grade : String
  status : String
  errors : Int
  warnings : Int
  infos : Int
} derive(Debug, Eq)

///|
pub fn Score::from_diagnostics(diagnostics : Array[Diagnostic]) -> Score {
  let mut errors = 0
  let mut warnings = 0
  let mut infos = 0
  for item in diagnostics {
    match item.severity {
      Error => errors += 1
      Warn => warnings += 1
      Info => infos += 1
    }
  }
  let (grade, status) = if errors > 0 {
    ("D", "fail")
  } else if warnings >= 3 {
    ("C", "warn")
  } else if warnings > 0 {
    ("B", "warn")
  } else {
    ("A", "pass")
  }
  { grade, status, errors, warnings, infos }
}

///|
/// Complete report data before rendering.
pub(all) struct DoctorReport {
  root : String
  diagnostics : Array[Diagnostic]
  score : Score
  profile : String
  checklist : Array[String]
} derive(Debug, Eq)

///|
pub fn DoctorReport::new(
  root : String,
  diagnostics : Array[Diagnostic],
) -> DoctorReport {
  {
    root,
    diagnostics,
    score: Score::from_diagnostics(diagnostics),
    profile: "default",
    checklist: default_release_checklist(),
  }
}

///|
pub fn DoctorReport::with_context(
  root : String,
  diagnostics : Array[Diagnostic],
  profile : String,
  checklist : Array[String],
) -> DoctorReport {
  {
    root,
    diagnostics,
    score: Score::from_diagnostics(diagnostics),
    profile,
    checklist,
  }
}

///|
pub fn DoctorReport::conclusion(self : DoctorReport) -> String {
  if self.score.errors > 0 || self.score.warnings > 0 {
    "Needs work"
  } else {
    "Ready for GitHub"
  }
}

///|
pub fn default_release_checklist() -> Array[String] {
  [
    "Review README.md and LICENSE", "Run moon check", "Run moon test", "Run moon package before publishing to mooncakes",
    "Push the repository with the prepared Git history",
  ]
}