///| Export helpers create plain-text artifacts suitable for audit attachments.

///|
pub fn csv_escape(value : String) -> String {
  if !(value.contains(",") || value.contains("\"") || value.contains("\n")) {
    return value
  }
  let mut escaped = "\""
  for char in value.to_array() {
    if char == '"' {
      escaped = escaped + "\"\""
    } else {
      escaped = escaped + char.to_string()
    }
  }
  escaped + "\""
}

///|
pub fn render_metrics_markdown(metrics : ResultMetrics) -> String {
  "# Reconciliation metrics\n\n" +
  "| Metric | Value |\n| --- | ---: |\n" +
  "| Expected minor units | \{metrics.expected_minor} |\n" +
  "| Captured minor units | \{metrics.captured_minor} |\n" +
  "| Refunded minor units | \{metrics.refunded_minor} |\n" +
  "| Outstanding minor units | \{metrics.outstanding_minor} |\n" +
  "| Settled orders | \{metrics.settled_orders} |\n" +
  "| Awaiting payment | \{metrics.awaiting_payment_orders} |\n" +
  "| Over-collected | \{metrics.over_collected_orders} |\n" +
  "| Refund risk | \{metrics.refund_risk_orders} |\n" +
  "| Errors | \{metrics.finding_metrics.error} |\n" +
  "| Warnings | \{metrics.finding_metrics.warning} |\n"
}

///|
pub fn render_code_counts_markdown(counts : Array[CodeCount]) -> String {
  let mut output = "# Finding counts\n\n| Code | Count |\n| --- | ---: |\n"
  for item in counts {
    output = output + "| \{finding_code_label(item.code)} | \{item.count} |\n"
  }
  output
}

///|
pub fn render_currency_totals_csv(totals : Array[CurrencyTotal]) -> String {
  let mut output = "currency,order_count,expected_minor,captured_minor,refunded_minor,outstanding_minor\n"
  for total in totals {
    output = output +
      "\{csv_escape(total.currency)},\{total.order_count},\{total.expected_minor},\{total.captured_minor},\{total.refunded_minor},\{total.outstanding_minor}\n"
  }
  output
}

///|
pub fn render_account_totals_csv(totals : Array[AccountTotal]) -> String {
  let mut output = "account_id,order_count,expected_minor,captured_minor,refunded_minor,outstanding_minor\n"
  for total in totals {
    output = output +
      "\{csv_escape(total.account_id)},\{total.order_count},\{total.expected_minor},\{total.captured_minor},\{total.refunded_minor},\{total.outstanding_minor}\n"
  }
  output
}

///|
pub fn render_age_buckets_csv(buckets : Array[AgeBucket]) -> String {
  let mut output = "age_bucket,order_count,outstanding_minor\n"
  for bucket in buckets {
    output = output +
      "\{csv_escape(bucket.label)},\{bucket.order_count},\{bucket.outstanding_minor}\n"
  }
  output
}

///|
pub fn render_findings_csv(findings : Array[Finding]) -> String {
  let mut output = "severity,code,record_kind,record_id,order_id,message\n"
  for finding in findings {
    let kind = match finding.record_kind {
      Order => "order"
      Payment => "payment"
      Refund => "refund"
      Adjustment => "adjustment"
    }
    output = output +
      "\{severity_label(finding.severity)},\{finding_code_label(finding.code)},\{kind},\{csv_escape(finding.record_id)},\{csv_escape(finding.order_id)},\{csv_escape(finding.message)}\n"
  }
  output
}

///|
pub fn render_review_queue_csv(queue : ReviewQueue) -> String {
  let mut output = "case_id,priority,state,owner,decision,record_id,order_id,code,note\n"
  for case in sort_cases_by_priority(queue.cases) {
    output = output +
      "\{csv_escape(case.case_id)},\{case.priority},\{review_state_label(case.state)},\{csv_escape(case.owner)},\{review_decision_label(case.decision)},\{csv_escape(case.finding.record_id)},\{csv_escape(case.finding.order_id)},\{finding_code_label(case.finding.code)},\{csv_escape(case.resolution_note)}\n"
  }
  output
}

///|
pub fn render_allocation_csv(plan : AllocationPlan) -> String {
  let mut output = "payment_id,order_id,currency,allocated_minor,state,note\n"
  for allocation in plan.allocations {
    output = output +
      "\{csv_escape(allocation.payment_id)},\{csv_escape(allocation.order_id)},\{csv_escape(allocation.currency)},\{allocation.allocated_minor},\{allocation_state_label(allocation.state)},\{csv_escape(allocation.note)}\n"
  }
  output
}

///|
pub fn render_quality_issues_csv(report : InputQualityReport) -> String {
  let mut output = "source,row_number,severity,code,record_id,order_id,message\n"
  for issue in report.issues {
    output = output +
      "\{source_kind_label(issue.source)},\{issue.row_number},\{severity_label(issue.severity)},\{quality_issue_code_label(issue.code)},\{csv_escape(issue.record_id)},\{csv_escape(issue.order_id)},\{csv_escape(issue.message)}\n"
  }
  output
}

///|
pub fn render_audit_bundle(
  input : LedgerInput,
  result : ReconciliationResult,
  current_day : Int,
) -> String {
  let metrics = result_metrics(result)
  let queue = queue_from_result(result, current_day)
  let allocation = allocate_payments(input, result)
  let quality = inspect_input_quality(input, default_policy(), current_day)
  render_metrics_markdown(metrics) +
  "\n" +
  render_code_counts_markdown(finding_code_counts(result.findings)) +
  "\n" +
  "## Review queue\n\n```\n" +
  render_review_queue(queue) +
  "```\n\n" +
  "## Allocation plan\n\n```\n" +
  render_allocation_plan(allocation) +
  "```\n\n" +
  "## Input quality\n\n```\n" +
  render_quality_report(quality) +
  "```\n"
}

///|
pub fn export_is_safe_csv(text : String) -> Bool {
  !text.contains("\r")
}