///|
fn status_text(status : SolveStatus) -> String {
  match status {
    Solved => "solved"
    Partial => "partial"
    Infeasible => "infeasible"
    BudgetExceeded => "budget-exceeded"
  }
}

///|
fn diagnostic_code_text(code : DiagnosticCode) -> String {
  match code {
    InvalidTimeSpan => "invalid-time-span"
    DuplicatePerson => "duplicate-person"
    DuplicateShift => "duplicate-shift"
    UnknownPerson => "unknown-person"
    UnknownShift => "unknown-shift"
    InvalidSlot => "invalid-slot"
    DuplicateAssignment => "duplicate-assignment"
    OverlappingAssignment => "overlapping-assignment"
    OutsideAvailability => "outside-availability"
    SkillMismatch => "skill-mismatch"
    MaximumMinutesExceeded => "maximum-minutes-exceeded"
    InsufficientRest => "insufficient-rest"
    ConsecutiveLimitExceeded => "consecutive-limit-exceeded"
    UncoveredSlot => "uncovered-slot"
    EmptyCandidateDomain => "empty-candidate-domain"
    SearchBudgetExceeded => "search-budget-exceeded"
    InvalidConfiguration => "invalid-configuration"
  }
}

///|
pub fn render_markdown(
  problem : ScheduleProblem,
  result : ScheduleResult,
) -> String {
  let mut text = "# ShiftWeave schedule report\n\n"
  text = text + "- Status: **\{status_text(result.status)}**\n"
  text = text +
    "- Covered slots: \{coverage_summary(problem, result.assignments).covered_slots}/\{coverage_summary(problem, result.assignments).total_slots}\n"
  text = text + "- Objective: \{result.metrics.objective}\n"
  text = text + "- Preference score: \{result.metrics.preference_score}\n"
  text = text + "- Fairness penalty: \{result.metrics.fairness_penalty}\n"
  text = text + "- Disruptions: \{result.metrics.disruption_count}\n\n"
  text = text +
    "## Assignments\n\n| Shift | Time | Slot | Person | Locked |\n|---|---|---:|---|---|\n"
  for shift in problem.shifts {
    for assignment in result.assignments {
      if assignment.shift_id == shift.id {
        text = text +
          "| \{shift.label} | \{minutes_to_clock(shift.time.start)}–\{minutes_to_clock(shift.time.end)} | \{assignment.slot + 1} | \{assignment.person_id} | \{assignment.locked} |\n"
      }
    }
  }
  text = text +
    "\n## Workloads\n\n| Person | Minutes | Target | Difference | Assignments | Preference |\n|---|---:|---:|---:|---:|---:|\n"
  for load in all_workloads(problem, result.assignments) {
    text = text +
      "| \{load.person_id} | \{load.assigned_minutes} | \{load.target_minutes} | \{load.difference} | \{load.assignment_count} | \{load.preference_score} |\n"
  }
  if result.diagnostics.length() > 0 {
    text = text + "\n## Diagnostics\n\n"
    for item in result.diagnostics {
      text = text + "- `\{diagnostic_code_text(item.code)}`: \{item.message}\n"
    }
  }
  text
}

///|
pub fn render_csv(problem : ScheduleProblem, result : ScheduleResult) -> String {
  let mut text = "shift_id,shift_label,start,end,slot,person_id,locked\n"
  for assignment in result.assignments {
    match find_shift(problem, assignment.shift_id) {
      Some(shift) =>
        text = text +
          "\{shift.id},\{shift.label},\{shift.time.start},\{shift.time.end},\{assignment.slot},\{assignment.person_id},\{assignment.locked}\n"
      None => ()
    }
  }
  text
}

///|
pub fn render_json(
  _problem : ScheduleProblem,
  result : ScheduleResult,
) -> String {
  let mut text = "{\"status\":\"\{status_text(result.status)}\",\"objective\":\{result.metrics.objective},\"assignments\":["
  let mut first = true
  for assignment in result.assignments {
    if !first {
      text = text + ","
    }
    first = false
    text = text +
      "{\"shift_id\":\"\{assignment.shift_id}\",\"slot\":\{assignment.slot},\"person_id\":\"\{assignment.person_id}\",\"locked\":\{assignment.locked}}"
  }
  text + "]}"
}