///|
pub(all) enum ImpactLevel {
  ImpactNone
  ImpactLow
  ImpactModerate
  ImpactHigh
  ImpactCritical
} derive(Eq, Debug)

///|
pub(all) struct IncidentReport {
  title : String
  minute : Int
  impact : ImpactLevel
  decision : GateDecision
  severity : Severity
  affected_users : Int
  affected_traffic_units : Int
  primary_reason : String
  actions : Array[String]
} derive(Eq, Debug)

///|
pub fn IncidentReport::IncidentReport(
  title? : String = "",
  minute? : Int = 0,
  impact? : ImpactLevel = ImpactNone,
  decision? : GateDecision = Advance,
  severity? : Severity = Pass,
  affected_users? : Int = 0,
  affected_traffic_units? : Int = 0,
  primary_reason? : String = "",
  actions? : Array[String] = [],
) -> IncidentReport {
  {
    title,
    minute,
    impact,
    decision,
    severity,
    affected_users,
    affected_traffic_units,
    primary_reason,
    actions,
  }
}

///|
pub fn incident_report(
  plan : RolloutPlan,
  users : Array[UserProfile],
  metrics : Array[MetricsSnapshot],
  minute : Int,
) -> IncidentReport {
  let blast = blast_radius_for_users(plan, users, minute)
  let metric = incident_nearest_metric(metrics, minute)
  let gate = evaluate_gate(plan.budget, metric, blast)
  let reason = if gate.reasons.length() == 0 {
    "no gate reason"
  } else {
    gate.reasons[0]
  }
  {
    title: plan.name + " minute " + minute.to_string(),
    minute,
    impact: impact_from_gate(gate, blast),
    decision: gate.decision,
    severity: gate.severity,
    affected_users: blast.exposed_users,
    affected_traffic_units: blast.exposed_traffic_units,
    primary_reason: reason,
    actions: incident_actions(gate, blast),
  }
}

///|
fn incident_nearest_metric(
  metrics : Array[MetricsSnapshot],
  minute : Int,
) -> MetricsSnapshot {
  if metrics.length() == 0 {
    return MetricsSnapshot::MetricsSnapshot(minute~, synthetic_pass=false)
  }
  let mut chosen = metrics[0]
  let mut best_delta = if metrics[0].minute > minute {
    metrics[0].minute - minute
  } else {
    minute - metrics[0].minute
  }
  for metric in metrics {
    let delta = if metric.minute > minute {
      metric.minute - minute
    } else {
      minute - metric.minute
    }
    if delta < best_delta {
      chosen = metric
      best_delta = delta
    }
  }
  chosen
}

///|
pub fn impact_text(impact : ImpactLevel) -> String {
  match impact {
    ImpactNone => "none"
    ImpactLow => "low"
    ImpactModerate => "moderate"
    ImpactHigh => "high"
    ImpactCritical => "critical"
  }
}

///|
fn impact_from_gate(gate : GateResult, blast : BlastRadius) -> ImpactLevel {
  if gate.decision == Advance && blast.exposed_users == 0 {
    ImpactNone
  } else if gate.decision == Advance {
    ImpactLow
  } else if gate.decision == Hold && blast.percent_bp < 1000 {
    ImpactLow
  } else if gate.decision == Hold {
    ImpactModerate
  } else if gate.decision == Rollback && blast.percent_bp < 2500 {
    ImpactHigh
  } else {
    ImpactCritical
  }
}

///|
fn incident_actions(gate : GateResult, blast : BlastRadius) -> Array[String] {
  let actions = Array::new(capacity=8)
  if gate.decision == Advance {
    actions.push("Keep collecting metrics for the next checkpoint.")
    actions.push("Do not change rollout salt during the healthy window.")
  } else if gate.decision == Hold {
    actions.push("Freeze the current phase and require manual release review.")
    actions.push(
      "Compare the current metric window with the previous healthy window.",
    )
    actions.push(
      "Keep customer-facing exposure at " +
      percent_text(blast.percent_bp) +
      " or lower.",
    )
  } else if gate.decision == Rollback {
    actions.push(
      "Disable the active feature flag or switch to the rollback phase.",
    )
    actions.push(
      "Record the first failing metric window in the incident notes.",
    )
    actions.push("Notify the owner before attempting another advance.")
  } else {
    actions.push("Abort the release and open an emergency follow-up record.")
    actions.push("Preserve all generated simulation evidence for review.")
  }
  if blast.exposed_traffic_units > 0 {
    actions.push(
      "Validate " +
      blast.exposed_traffic_units.to_string() +
      " exposed traffic units after mitigation.",
    )
  }
  actions
}

///|
pub fn incident_report_markdown(report : IncidentReport) -> String {
  let out = StringBuilder()
  out.write_string("# CanaryRing Incident Report\n\n")
  out.write_string("- Title: " + report.title + "\n")
  out.write_string("- Minute: " + report.minute.to_string() + "\n")
  out.write_string("- Impact: " + impact_text(report.impact) + "\n")
  out.write_string("- Decision: " + decision_text(report.decision) + "\n")
  out.write_string("- Severity: " + severity_text(report.severity) + "\n")
  out.write_string(
    "- Affected users: " + report.affected_users.to_string() + "\n",
  )
  out.write_string(
    "- Affected traffic units: " +
    report.affected_traffic_units.to_string() +
    "\n",
  )
  out.write_string("- Primary reason: " + report.primary_reason + "\n\n")
  out.write_string("## Actions\n\n")
  for action in report.actions {
    out.write_string("- " + action + "\n")
  }
  out.to_string()
}

///|
pub fn incident_report_json(report : IncidentReport) -> String {
  let out = StringBuilder()
  out.write_string("{")
  out.write_string("\"title\":\"" + json_escape(report.title) + "\",")
  out.write_string("\"minute\":" + report.minute.to_string() + ",")
  out.write_string("\"impact\":\"" + impact_text(report.impact) + "\",")
  out.write_string("\"decision\":\"" + decision_text(report.decision) + "\",")
  out.write_string("\"severity\":\"" + severity_text(report.severity) + "\",")
  out.write_string(
    "\"affected_users\":" + report.affected_users.to_string() + ",",
  )
  out.write_string(
    "\"affected_traffic_units\":" +
    report.affected_traffic_units.to_string() +
    ",",
  )
  out.write_string(
    "\"primary_reason\":\"" + json_escape(report.primary_reason) + "\",",
  )
  out.write_string("\"actions\":[")
  for i in 0.. 0 {
      out.write_string(",")
    }
    out.write_string("\"" + json_escape(report.actions[i]) + "\"")
  }
  out.write_string("]}")
  out.to_string()
}

///|
pub fn incident_requires_customer_note(report : IncidentReport) -> Bool {
  report.impact == ImpactHigh || report.impact == ImpactCritical
}

///|
pub fn customer_note_draft(report : IncidentReport) -> String {
  if !incident_requires_customer_note(report) {
    "No customer note required for this simulated impact level."
  } else {
    "We detected elevated risk during a controlled canary release, paused exposure, and are validating recovery before any wider rollout."
  }
}

///|
pub fn incident_score(report : IncidentReport) -> Int {
  let mut score = 100
  if report.impact == ImpactCritical {
    score -= 60
  } else if report.impact == ImpactHigh {
    score -= 40
  } else if report.impact == ImpactModerate {
    score -= 20
  } else if report.impact == ImpactLow {
    score -= 8
  }
  if report.decision == Rollback {
    score -= 10
  } else if report.decision == Abort {
    score -= 20
  }
  clamp(score, 0, 100)
}