///|
/// Composable production report for quality, drift, risk and forecast review.
pub struct AnalyticsReport {
  title : String
  generated_at : String
  sections : Array[ReportSection]
  alerts : Int
  score : Double
}

///|
pub struct ReportSection {
  name : String
  status : String
  score : Double
  metrics : Array[ReportMetric]
  notes : Array[String]
}

///|
pub struct ReportMetric {
  name : String
  value : Double
  unit : String
  healthy : Bool
}

///|
pub fn report_metric(
  name : String,
  value : Double,
  unit : String,
  healthy : Bool,
) -> ReportMetric {
  { name, value, unit, healthy }
}

///|
pub fn report_section(
  name : String,
  status : String,
  score : Double,
  metrics : Array[ReportMetric],
  notes : Array[String],
) -> ReportSection {
  { name, status, score, metrics, notes }
}

///|
pub fn analytics_report(
  title : String,
  generated_at : String,
) -> AnalyticsReport {
  { title, generated_at, sections: [], alerts: 0, score: 1.0 }
}

///|
fn report_recalculate(report : AnalyticsReport) -> AnalyticsReport {
  if report.sections.length() == 0 {
    return { ..report, score: 1.0 }
  }
  let mut total = 0.0
  let mut alerts = 0
  for section in report.sections {
    total += section.score
    if section.status != "healthy" {
      alerts += 1
    }
  }
  { ..report, alerts, score: total / report.sections.length().to_double() }
}

///|
pub fn report_add_section(
  report : AnalyticsReport,
  section : ReportSection,
) -> AnalyticsReport {
  report.sections.push(section)
  report_recalculate(report)
}

///|
pub fn report_add_quality(
  report : AnalyticsReport,
  quality : QualityReport,
) -> AnalyticsReport {
  let metrics = [
    report_metric(
      "completeness",
      quality.completeness,
      "ratio",
      quality.completeness >= 0.99,
    ),
    report_metric(
      "duplicate_fraction",
      quality.duplicate_fraction,
      "ratio",
      quality.duplicate_fraction <= 0.01,
    ),
    report_metric(
      "quality_score",
      quality.quality_score,
      "ratio",
      quality.quality_score >= 0.95,
    ),
  ]
  report_add_section(
    report,
    report_section(
      "quality",
      if quality.quality_score >= 0.95 {
        "healthy"
      } else {
        "review"
      },
      quality.quality_score,
      metrics,
      [],
    ),
  )
}

///|
pub fn report_add_drift(
  report : AnalyticsReport,
  drift : DriftReport,
) -> AnalyticsReport {
  let score = 1.0 - transform_clip(drift.psi, 0.0, 1.0)
  let metrics = [
    report_metric("psi", drift.psi, "score", drift.psi < 0.2),
    report_metric(
      "js_divergence",
      drift.js_divergence,
      "score",
      drift.js_divergence < 0.1,
    ),
    report_metric(
      "ks_statistic",
      drift.ks_statistic,
      "score",
      drift.ks_statistic < 0.1,
    ),
    report_metric(
      "wasserstein",
      drift.wasserstein,
      "distance",
      drift.wasserstein < 1.0,
    ),
  ]
  report_add_section(
    report,
    report_section(
      "drift",
      if score >= 0.8 {
        "healthy"
      } else {
        "review"
      },
      score,
      metrics,
      [],
    ),
  )
}

///|
pub fn report_add_risk(
  report : AnalyticsReport,
  risk : RiskSnapshot,
) -> AnalyticsReport {
  let score = transform_clip(1.0 - risk.maximum_drawdown, 0.0, 1.0)
  let metrics = [
    report_metric("center", risk.center, "return", true),
    report_metric("scale", risk.scale, "return", risk.scale < 1.0),
    report_metric(
      "value_at_risk",
      risk.value_at_risk,
      "return",
      risk.value_at_risk < 0.2,
    ),
    report_metric(
      "conditional_var",
      risk.conditional_var,
      "return",
      risk.conditional_var < 0.2,
    ),
    report_metric(
      "max_drawdown",
      risk.maximum_drawdown,
      "ratio",
      risk.maximum_drawdown < 0.2,
    ),
    report_metric("sharpe", risk.sharpe, "ratio", risk.sharpe >= 0.0),
  ]
  report_add_section(
    report,
    report_section(
      "risk",
      if score >= 0.8 {
        "healthy"
      } else {
        "review"
      },
      score,
      metrics,
      [],
    ),
  )
}

///|
pub fn report_add_forecast(
  report : AnalyticsReport,
  forecast : ForecastReport,
) -> AnalyticsReport {
  let score = transform_clip(forecast.coverage, 0.0, 1.0)
  let metrics = [
    report_metric("mae", forecast.mae, "error", forecast.mae >= 0.0),
    report_metric("rmse", forecast.rmse, "error", forecast.rmse >= 0.0),
    report_metric(
      "coverage",
      forecast.coverage,
      "ratio",
      forecast.coverage >= 0.8,
    ),
    report_metric(
      "bias",
      forecast.bias,
      "error",
      abs_double(forecast.bias) < forecast.rmse + 1.0e-12,
    ),
  ]
  report_add_section(
    report,
    report_section(
      "forecast",
      if forecast.coverage >= 0.8 {
        "healthy"
      } else {
        "review"
      },
      score,
      metrics,
      [],
    ),
  )
}

///|
pub fn report_add_validation(
  report : AnalyticsReport,
  validation : ValidationReport,
) -> AnalyticsReport {
  let status = if validation_has_errors(validation) {
    "review"
  } else {
    "healthy"
  }
  let metrics = [
    report_metric(
      "validation_score",
      validation.score,
      "ratio",
      validation.score >= 0.95,
    ),
    report_metric(
      "failed_checks",
      validation.failed.to_double(),
      "count",
      validation.failed == 0,
    ),
  ]
  report_add_section(
    report,
    report_section(
      "validation",
      status,
      validation.score,
      metrics,
      validation_report_lines(validation),
    ),
  )
}

///|
pub fn report_section_status(section : ReportSection) -> String {
  section.status
}

///|
pub fn report_healthy_sections(report : AnalyticsReport) -> Int {
  let mut count = 0
  for section in report.sections {
    if section.status == "healthy" {
      count += 1
    }
  }
  count
}

///|
pub fn report_metric_count(report : AnalyticsReport) -> Int {
  let mut count = 0
  for section in report.sections {
    count += section.metrics.length()
  }
  count
}

///|
pub fn report_alert_count(report : AnalyticsReport) -> Int {
  report.alerts
}

///|
pub fn report_metric_rows(report : AnalyticsReport) -> Array[Array[String]] {
  let rows = [["section", "metric", "value", "unit", "healthy"]]
  for section in report.sections {
    for metric in section.metrics {
      rows.push([
        section.name,
        metric.name,
        metric.value.to_string(),
        metric.unit,
        metric.healthy.to_string(),
      ])
    }
  }
  rows
}

///|
pub fn report_metric_table(report : AnalyticsReport) -> String {
  let lines = []
  for row in report_metric_rows(report) {
    lines.push(row.join("\t"))
  }
  lines.join("\n")
}

///|
pub fn report_summary_lines(report : AnalyticsReport) -> Array[String] {
  [
    "title=" + report.title,
    "generated_at=" + report.generated_at,
    "sections=" + report.sections.length().to_string(),
    "healthy_sections=" + report_healthy_sections(report).to_string(),
    "alerts=" + report.alerts.to_string(),
    "score=" + report.score.to_string(),
  ]
}

///|
pub fn report_lines(report : AnalyticsReport) -> Array[String] {
  let lines = report_summary_lines(report)
  for section in report.sections {
    lines.push(
      "[" +
      section.name +
      "] status=" +
      section.status +
      " score=" +
      section.score.to_string(),
    )
    for metric in section.metrics {
      lines.push(
        "metric=" +
        metric.name +
        " value=" +
        metric.value.to_string() +
        " unit=" +
        metric.unit +
        " healthy=" +
        metric.healthy.to_string(),
      )
    }
    for note in section.notes {
      lines.push("note=" + note)
    }
  }
  lines
}

///|
pub fn report_string(report : AnalyticsReport) -> String {
  report_lines(report).join("\n")
}

///|
pub fn report_quality_score(report : AnalyticsReport) -> Double {
  report.score
}

///|
pub fn report_is_healthy(report : AnalyticsReport, threshold : Double) -> Bool {
  report.score >= threshold && report.alerts == 0
}

///|
pub fn report_section_scores(report : AnalyticsReport) -> Array[Double] {
  let result = []
  for section in report.sections {
    result.push(section.score)
  }
  result
}

///|
pub fn report_section_names(report : AnalyticsReport) -> Array[String] {
  let result = []
  for section in report.sections {
    result.push(section.name)
  }
  result
}

///|
pub fn report_compare(
  left : AnalyticsReport,
  right : AnalyticsReport,
) -> Array[Double] {
  [
    left.score,
    right.score,
    right.score - left.score,
    left.alerts.to_double(),
    right.alerts.to_double(),
  ]
}

///|
pub fn report_from_data(data : Array[Double], name : String) -> AnalyticsReport {
  let quality = quality_report(data, quality_default_rule())
  let validation = validation_summary(data, name)
  report_add_validation(
    report_add_quality(analytics_report(name, "local"), quality),
    validation,
  )
}

///|
pub fn report_from_baseline(
  baseline : Array[Double],
  current : Array[Double],
  name : String,
) -> AnalyticsReport {
  let first = report_from_data(current, name)
  let drift = drift_report(baseline, current, drift_default_rule())
  report_add_drift(first, drift)
}

///|
pub fn report_alert_messages(report : AnalyticsReport) -> Array[String] {
  let result = []
  for section in report.sections {
    if section.status != "healthy" {
      result.push(section.name + " requires review")
    }
    for metric in section.metrics {
      if !metric.healthy {
        result.push(section.name + ":" + metric.name)
      }
    }
  }
  result
}

///|
pub fn report_digest(report : AnalyticsReport) -> Array[Double] {
  [
    report.score,
    report.sections.length().to_double(),
    report_healthy_sections(report).to_double(),
    report.alerts.to_double(),
    report_metric_count(report).to_double(),
  ]
}

///|
pub fn report_render_markdown(report : AnalyticsReport) -> String {
  let lines = [
    "# " + report.title,
    "",
    "- Generated: " + report.generated_at,
    "- Score: " + report.score.to_string(),
    "- Alerts: " + report.alerts.to_string(),
    "",
  ]
  for section in report.sections {
    lines.push("## " + section.name)
    lines.push("Status: " + section.status + "  ")
    lines.push("Score: " + section.score.to_string())
    for metric in section.metrics {
      lines.push(
        "- " + metric.name + ": " + metric.value.to_string() + " " + metric.unit,
      )
    }
    lines.push("")
  }
  lines.join("\n")
}

///|
pub fn report_render_json_fields(report : AnalyticsReport) -> Array[String] {
  [
    "title=" + report.title,
    "generated_at=" + report.generated_at,
    "score=" + report.score.to_string(),
    "alerts=" + report.alerts.to_string(),
    "metric_count=" + report_metric_count(report).to_string(),
  ]
}