///|
/// One independently scored health dimension in a brand book review.
pub struct QualityDimension {
  name : String
  score : Int
  summary : String
} derive(Debug, Eq)

///|
pub fn QualityDimension::new(
  name : String,
  score : Int,
  summary : String,
) -> QualityDimension {
  { name, score: clamp_score(score), summary }
}

///|
pub fn QualityDimension::passed(
  self : QualityDimension,
  threshold : Int,
) -> Bool {
  self.score >= threshold
}

///|
pub fn QualityDimension::label(self : QualityDimension) -> String {
  self.name + " (" + self.score.to_string() + "/100)"
}

///|
pub fn QualityDimension::to_json(self : QualityDimension) -> String {
  "{\"name\":\"" +
  json_escape(self.name) +
  "\",\"score\":" +
  self.score.to_string() +
  ",\"summary\":\"" +
  json_escape(self.summary) +
  "\"}"
}

///|
pub struct BrandQualityReport {
  score : Int
  dimensions : Array[QualityDimension]
  audit : AuditReport
} derive(Debug, Eq)

///|
pub fn BrandQualityReport::from_brand_book(
  book : BrandBook,
) -> BrandQualityReport {
  let validation = book.validate_with_policy(ValidationPolicy::strict())
  let accessibility = book.accessibility_score()
  let asset = book.asset_health()
  let component = book.component_score()
  let typography = typography_dimension_score(book.typography)
  let documentation = documentation_score(book)
  let export_score = export_dimension_score(book)
  let dimensions : Array[QualityDimension] = [
    QualityDimension::new(
      "validation",
      score_from_report(validation),
      validation.summary(),
    ),
    QualityDimension::new(
      "accessibility", accessibility, "contrast pairs and readable text guidance",
    ),
    QualityDimension::new(
      "assets", asset, "logo safety and asset policy checks",
    ),
    QualityDimension::new(
      "components", component, "component contract completeness",
    ),
    QualityDimension::new(
      "typography", typography, "scale rhythm and font fallbacks",
    ),
    QualityDimension::new(
      "documentation", documentation, "metadata, voice, misuse guidance, and examples",
    ),
    QualityDimension::new(
      "exports", export_score, "stable machine-readable output formats",
    ),
  ]
  let total = dimensions.fold(init=0, fn(sum, dimension) {
    sum + dimension.score
  })
  {
    score: if dimensions.length() == 0 {
      0
    } else {
      total / dimensions.length()
    },
    dimensions,
    audit: validation,
  }
}

///|
pub fn BrandQualityReport::passed(
  self : BrandQualityReport,
  threshold : Int,
) -> Bool {
  self.score >= threshold && !self.audit.has_errors()
}

///|
pub fn BrandQualityReport::dimension(
  self : BrandQualityReport,
  name : String,
) -> QualityDimension? {
  let target = normalize_identifier(name)
  for dimension in self.dimensions {
    if normalize_identifier(dimension.name) == target {
      return Some(dimension)
    }
  }
  None
}

///|
pub fn BrandQualityReport::weakest_dimension(
  self : BrandQualityReport,
) -> QualityDimension? {
  if self.dimensions.length() == 0 {
    None
  } else {
    let mut weakest = self.dimensions[0]
    for dimension in self.dimensions {
      if dimension.score < weakest.score {
        weakest = dimension
      }
    }
    Some(weakest)
  }
}

///|
pub fn BrandQualityReport::strongest_dimension(
  self : BrandQualityReport,
) -> QualityDimension? {
  if self.dimensions.length() == 0 {
    None
  } else {
    let mut strongest = self.dimensions[0]
    for dimension in self.dimensions {
      if dimension.score > strongest.score {
        strongest = dimension
      }
    }
    Some(strongest)
  }
}

///|
pub fn BrandQualityReport::dimension_names(
  self : BrandQualityReport,
) -> Array[String] {
  self.dimensions.map(fn(dimension) { dimension.name })
}

///|
pub fn BrandQualityReport::to_json(self : BrandQualityReport) -> String {
  "{\"score\":" +
  self.score.to_string() +
  ",\"dimensions\":[" +
  self.dimensions.map(fn(dimension) { dimension.to_json() }).join(",") +
  "]}"
}

///|
pub fn BrandQualityReport::to_markdown(self : BrandQualityReport) -> String {
  let lines : Array[String] = [
    "# Quality report",
    "",
    "Overall score: **" + self.score.to_string() + "/100**",
    "",
    "| Dimension | Score | Assessment |",
    "| --- | ---: | --- |",
  ]
  for dimension in self.dimensions {
    lines.push(
      "| " +
      dimension.name +
      " | " +
      dimension.score.to_string() +
      " | " +
      dimension.summary +
      " |",
    )
  }
  lines.push("")
  lines.push("Validation: " + self.audit.summary())
  lines.join("\n")
}

///|
pub fn BrandQualityReport::to_badges(
  self : BrandQualityReport,
) -> Array[String] {
  let result : Array[String] = []
  for dimension in self.dimensions {
    let level = if dimension.score >= 90 {
      "excellent"
    } else if dimension.score >= 70 {
      "healthy"
    } else if dimension.score >= 50 {
      "review"
    } else {
      "critical"
    }
    result.push(dimension.name + ":" + level)
  }
  result
}

///|
pub fn BrandQualityReport::action_items(
  self : BrandQualityReport,
) -> Array[String] {
  let result : Array[String] = []
  for dimension in self.dimensions {
    if dimension.score < 80 {
      result.push("Improve " + dimension.name + ": " + dimension.summary)
    }
  }
  if self.audit.has_errors() {
    result.push("Resolve validation errors before publishing")
  }
  result
}

///|
pub fn BrandQualityReport::score_label(self : BrandQualityReport) -> String {
  if self.score >= 90 {
    "excellent"
  } else if self.score >= 75 {
    "healthy"
  } else if self.score >= 60 {
    "needs review"
  } else {
    "not ready"
  }
}

///|
pub fn BrandQualityReport::audit_markdown(self : BrandQualityReport) -> String {
  self.audit.to_markdown()
}

///|
fn typography_dimension_score(typography : Typography) -> Int {
  let report = typography.audit_rhythm(TypographyPolicy::strict())
  if report.has_errors() {
    50
  } else if typography.accessible_step_count() == typography.scale.length() {
    100
  } else {
    85
  }
}

///|
fn documentation_score(book : BrandBook) -> Int {
  let mut score = 0
  if book.name.trim().to_owned() != "" {
    score += 20
  }
  if book.tagline.trim().to_owned() != "" {
    score += 15
  }
  if book.voice.tone.trim().to_owned() != "" {
    score += 15
  }
  if book.voice.writing_principles.length() >= 2 {
    score += 15
  }
  if book.forbidden.length() >= 2 {
    score += 15
  }
  if book.metadata.source.trim().to_owned() != "" {
    score += 10
  }
  if book.metadata.license.trim().to_owned() != "" {
    score += 10
  }
  score
}

///|
fn export_dimension_score(book : BrandBook) -> Int {
  let manifest = ExportManifest::for_brand_book(book)
  if manifest.len() >= 6 && manifest.total_bytes() > 0 {
    100
  } else if manifest.len() >= 3 {
    75
  } else {
    40
  }
}

///|
fn score_from_report(report : AuditReport) -> Int {
  let mut score = 100 - report.error_count() * 25 - report.warning_count() * 5
  if score < 0 {
    score = 0
  }
  score
}

///|
fn clamp_score(value : Int) -> Int {
  if value < 0 {
    0
  } else if value > 100 {
    100
  } else {
    value
  }
}