///|
/// Kind of deterministic replay mismatch.
pub(all) enum ReplayIssueKind {
  PartIdentifier
  StepCount
  FeatureIdentifier
  DeviceIdentifier
  Strategy
  Direction
  Uncertainty
  Coverage
  Capability
} derive(Eq, Debug)

///|
/// One replay mismatch with an index and stable message.
pub(all) struct ReplayIssue {
  kind : ReplayIssueKind
  index : Int
  message : String
}

///|
/// Result of comparing an expected and actual plan.
pub(all) struct ReplayResult {
  expected_part_id : String
  actual_part_id : String
  expected_steps : Int
  actual_steps : Int
  matches : Bool
  issues : Array[ReplayIssue]
}

///|
/// Compare two plans field by field.
pub fn compare_plans(
  expected : MeasurementPlan,
  actual : MeasurementPlan,
) -> ReplayResult {
  let issues = []
  if expected.part_id != actual.part_id {
    issues.push({
      kind: ReplayIssueKind::PartIdentifier,
      index: -1,
      message: "part identifier differs",
    })
  }
  if expected.steps.length() != actual.steps.length() {
    issues.push({
      kind: ReplayIssueKind::StepCount,
      index: -1,
      message: "step count differs",
    })
  }
  let common = if expected.steps.length() < actual.steps.length() {
    expected.steps.length()
  } else {
    actual.steps.length()
  }
  for i in 0.. ReplayResult {
  let rebuilt = MeasurementPlan::{
    part_id: plan.part_id,
    steps: plan.steps,
    summary: plan.summary,
  }
  compare_plans(plan, rebuilt)
}

///|
/// Return the number of replay issues.
pub fn replay_issue_count(result : ReplayResult) -> Int {
  result.issues.length()
}

///|
/// Return whether a replay has no differences.
pub fn replay_is_idempotent(plan : MeasurementPlan) -> Bool {
  replay_plan(plan).matches
}

///|
/// Return whether a replay contains a given issue kind.
pub fn replay_has_issue(result : ReplayResult, kind : ReplayIssueKind) -> Bool {
  let mut found = false
  for issue in result.issues {
    if issue.kind == kind {
      found = true
    }
  }
  found
}

///|
/// Return stable labels for replay issue kinds.
pub fn replay_issue_kind_text(kind : ReplayIssueKind) -> String {
  match kind {
    PartIdentifier => "part-id"
    StepCount => "step-count"
    FeatureIdentifier => "feature-id"
    DeviceIdentifier => "device-id"
    Strategy => "strategy"
    Direction => "direction"
    Uncertainty => "uncertainty"
    Coverage => "coverage"
    Capability => "capability"
  }
}

///|
/// Render replay issues as stable text.
pub fn render_replay_issues(result : ReplayResult) -> String {
  let mut output = ""
  for issue in result.issues {
    output = output +
      replay_issue_kind_text(issue.kind) +
      "[" +
      issue.index.to_string() +
      "] " +
      issue.message +
      "\n"
  }
  output
}