///|
fn csv_escape(value : String) -> String {
  let chars = ['"']
  for character in value.to_array() {
    if character == '"' {
      chars.push('"')
    }
    chars.push(character)
  }
  chars.push('"')
  String::from_array(chars)
}

///|
fn csv_text(value : String) -> String {
  if value.contains(",") || value.contains("\"") || value.contains("\n") {
    csv_escape(value)
  } else {
    value
  }
}

///|
/// Export features using the documented feature CSV contract.
pub fn export_features_csv(features : Array[Feature]) -> String {
  let mut output = "id,kind,nominal,lower,upper,datum\n"
  for feature in features {
    output = output +
      csv_text(feature.id) +
      "," +
      feature_kind_name(feature.kind) +
      "," +
      feature.nominal.to_string() +
      "," +
      feature.lower.to_string() +
      "," +
      feature.upper.to_string() +
      "," +
      feature.datum.to_string() +
      "\n"
  }
  output
}

///|
/// Export observations with their sample counts.
pub fn export_observations_csv(observations : Array[Observation]) -> String {
  let mut output = "feature_id,value,sample_count\n"
  for observation in observations {
    output = output +
      csv_text(observation.feature_id) +
      "," +
      observation.value.to_string() +
      "," +
      observation.sample_count.to_string() +
      "\n"
  }
  output
}

///|
/// Export diagnostics for machine review.
pub fn export_diagnostics_csv(diagnostics : Array[Diagnostic]) -> String {
  let mut output = "severity,code,message,index\n"
  for diagnostic in diagnostics {
    output = output +
      severity_text(diagnostic.severity) +
      "," +
      csv_text(diagnostic.code) +
      "," +
      csv_text(diagnostic.message) +
      "," +
      diagnostic.index.to_string() +
      "\n"
  }
  output
}

///|
/// Export plan steps with stable strategy and uncertainty fields.
pub fn export_plan_csv(plan : MeasurementPlan) -> String {
  let mut output = "part_id,feature_id,device_id,strategy,direction,uncertainty\n"
  for step in plan.steps {
    output = output +
      csv_text(plan.part_id) +
      "," +
      csv_text(step.feature_id) +
      "," +
      csv_text(step.device_id) +
      "," +
      strategy_text(step.strategy) +
      "," +
      step.direction +
      "," +
      step.uncertainty.to_string() +
      "\n"
  }
  output
}

///|
/// Export one batch summary as a single stable record.
pub fn export_batch_summary_csv(summary : BatchSummary) -> String {
  "feature_id,count,pass_count,fail_count,unmeasured_count,mean,range,stddev,defect_rate,cp,cpk\n" +
  csv_text(summary.feature_id) +
  "," +
  summary.count.to_string() +
  "," +
  summary.pass_count.to_string() +
  "," +
  summary.fail_count.to_string() +
  "," +
  summary.unmeasured_count.to_string() +
  "," +
  summary.mean.to_string() +
  "," +
  summary.range.to_string() +
  "," +
  summary.standard_deviation.to_string() +
  "," +
  summary.defect_rate.to_string() +
  "," +
  summary.capability.cp.to_string() +
  "," +
  summary.capability.cpk.to_string() +
  "\n"
}

///|
/// Export a trace with lifecycle and step fields.
pub fn export_trace_csv(log : TraceLog) -> String {
  let mut output = "part_id,event_kind,feature_id,step_index,message\n"
  for event in log.events {
    output = output +
      csv_text(event.part_id) +
      "," +
      trace_event_kind_text(event.kind) +
      "," +
      csv_text(event.feature_id) +
      "," +
      event.step_index.to_string() +
      "," +
      csv_text(event.message) +
      "\n"
  }
  output
}

///|
/// Return a stable label for a trace event kind.
pub fn trace_event_kind_text(kind : TraceEventKind) -> String {
  match kind {
    PlanStarted => "plan-started"
    PlanStep => "plan-step"
    ObservationRecorded => "observation-recorded"
    DecisionIssued => "decision-issued"
    WarningIssued => "warning-issued"
    PlanFinished => "plan-finished"
  }
}