///|
pub(all) enum ReportFormat {
  TextReport
  MarkdownReport
  JsonReport
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct RunnerCommand {
  phase : CommandPhase
  command : String
  timeout_ms : Int
} derive(Debug, Eq, ToJson, FromJson)

///|
pub(all) struct MutestConfig {
  include_kinds : Array[MutationKind]
  include_labels : Array[String]
  line_start : Int?
  line_end : Int?
  commands : Array[RunnerCommand]
  fail_fast : Bool
  max_mutants : Int?
  report_format : ReportFormat
} derive(Debug, Eq, ToJson, FromJson)

///|
pub fn default_commands() -> Array[RunnerCommand] {
  [
    {
      phase: CheckPhase,
      command: "moon check --warn-list +73",
      timeout_ms: 30000,
    },
    {
      phase: TestPhase,
      command: "moon test --warn-list +73",
      timeout_ms: 60000,
    },
  ]
}

///|
pub fn MutestConfig::default() -> MutestConfig {
  {
    include_kinds: [],
    include_labels: [],
    line_start: None,
    line_end: None,
    commands: default_commands(),
    fail_fast: true,
    max_mutants: None,
    report_format: TextReport,
  }
}

///|
pub fn MutestConfig::to_filter(self : MutestConfig) -> MutationFilter {
  {
    kinds: self.include_kinds,
    labels: self.include_labels,
    line_start: self.line_start,
    line_end: self.line_end,
  }
}

///|
pub fn parse_config_json(text : String) -> MutestConfig raise {
  @json.from_json(@json.parse(text))
}

///|
pub fn config_to_json_string(
  config : MutestConfig,
  indent? : Int = 2,
) -> String {
  config.to_json().stringify(indent~)
}

///|
pub fn command_phase_label(phase : CommandPhase) -> String {
  match phase {
    CheckPhase => "check"
    TestPhase => "test"
  }
}

///|
pub fn format_runner_command(command : RunnerCommand) -> String {
  "\{command_phase_label(command.phase)}: \{command.command} (\{command.timeout_ms} ms)"
}

///|
pub fn format_config(config : MutestConfig) -> String {
  let lines : Array[String] = [
    "Mutation config",
    "fail-fast: \{config.fail_fast}",
    "max-mutants: \{format_optional_int(config.max_mutants)}",
    "report-format: \{report_format_label(config.report_format)}",
    "include-kinds: \{format_kind_list(config.include_kinds)}",
    "include-labels: \{format_string_list(config.include_labels)}",
    "line-range: \{format_line_range(config.line_start, config.line_end)}",
    "commands:",
  ]
  for command in config.commands {
    lines.push("- " + format_runner_command(command))
  }
  lines.join("\n")
}

///|
pub fn report_format_label(format : ReportFormat) -> String {
  match format {
    TextReport => "text"
    MarkdownReport => "markdown"
    JsonReport => "json"
  }
}

///|
fn format_optional_int(value : Int?) -> String {
  match value {
    Some(n) => n.to_string()
    None => "unlimited"
  }
}

///|
fn format_line_range(start : Int?, end : Int?) -> String {
  match (start, end) {
    (Some(a), Some(b)) => "\{a}..\{b}"
    (Some(a), None) => "\{a}.."
    (None, Some(b)) => "..\{b}"
    (None, None) => "all"
  }
}

///|
fn format_kind_list(kinds : ArrayView[MutationKind]) -> String {
  if kinds.is_empty() {
    "all"
  } else {
    [ for kind in kinds => mutation_kind_label(kind) ].join(", ")
  }
}

///|
fn format_string_list(items : ArrayView[String]) -> String {
  if items.is_empty() {
    "all"
  } else {
    items.join(", ")
  }
}