///|
/// Pure command dispatcher used by the `sdp-tool` executable and tests.
fn cli_failure(kind : String, message : String) -> String {
  "{\"ok\":false,\"error\":{\"kind\":\"\{json_escape(kind)}\",\"message\":\"\{json_escape(message)}\"}}"
}

///|
fn cli_parse_failure(error : SdpError) -> String {
  "{\"ok\":false,\"error\":{\"stage\":\"\{json_escape(error.stage())}\",\"kind\":\"\{json_escape(error.kind())}\",\"line\":\{error.line()},\"column\":\{error.column()},\"context\":\"\{json_escape(error.context())}\",\"message\":\"\{json_escape(error.message())}\"}}"
}

///|
fn codecs_json(document : SessionDescription) -> String {
  let sb = StringBuilder()
  sb.write_char('[')
  for index, name in codec_names(document) {
    if index > 0 {
      sb.write_char(',')
    }
    sb.write_string("\"\{json_escape(name)}\"")
  }
  sb.write_char(']')
  sb.to_string()
}

///|
fn cli_help() -> String {
  "{\"ok\":true,\"commands\":[\"parse\",\"validate\",\"canonicalize\",\"inspect\",\"stats\",\"audit\",\"offer-answer\",\"version\",\"help\"],\"usage\":\"sdp-tool   [answer-sdp]\"}"
}

///|
fn cli_parse(input : String) -> String {
  match parse(input) {
    Err(error) => cli_parse_failure(error)
    Ok(document) => {
      let stats = statistics(document)
      "{\"ok\":true,\"version\":\{document.version()},\"session_name\":\"\{json_escape(document.session_name())}\",\"media\":\{stats.media_count()},\"attributes\":\{stats.session_attribute_count() + stats.media_attribute_count()}}"
    }
  }
}

///|
fn cli_validate(input : String) -> String {
  match parse(input) {
    Err(error) => cli_parse_failure(error)
    Ok(document) => {
      let report = validate(document)
      "{\"ok\":true,\"valid\":\{report.is_valid()},\"errors\":\{report.error_count()},\"warnings\":\{report.warning_count()},\"info\":\{report.info_count()}}"
    }
  }
}

///|
fn cli_canonicalize(input : String) -> String {
  match canonicalize(input) {
    Err(error) => cli_parse_failure(error)
    Ok(output) => "{\"ok\":true,\"sdp\":\"\{json_escape(output)}\"}"
  }
}

///|
fn cli_inspect(input : String) -> String {
  match parse(input) {
    Err(error) => cli_parse_failure(error)
    Ok(document) => {
      let stats = statistics(document)
      "{\"ok\":true,\"session\":\"\{json_escape(document.session_name())}\",\"media\":\{stats.media_count()},\"active\":\{stats.active_media_count()},\"rejected\":\{stats.rejected_media_count()},\"codecs\":\{codecs_json(document)}}"
    }
  }
}

///|
fn cli_stats(input : String) -> String {
  match parse(input) {
    Err(error) => cli_parse_failure(error)
    Ok(document) =>
      "{\"ok\":true,\"summary\":\"\{json_escape(statistics(document).to_text())}\"}"
  }
}

///|
fn cli_audit(input : String) -> String {
  match parse(input) {
    Err(error) => cli_parse_failure(error)
    Ok(document) => {
      let report = audit(document)
      "{\"ok\":true,\"high\":\{report.high_count()},\"medium\":\{report.medium_count()},\"low\":\{report.low_count()},\"findings\":\{report.finding_count()}}"
    }
  }
}

///|
fn cli_offer_answer(offer_input : String, answer_input : String) -> String {
  let offer = match parse(offer_input) {
    Err(error) => return cli_parse_failure(error)
    Ok(value) => value
  }
  let answer = match parse(answer_input) {
    Err(error) => return cli_parse_failure(error)
    Ok(value) => value
  }
  let report = analyze_offer_answer(offer, answer)
  "{\"ok\":true,\"compatible\":\{report.compatible()},\"issues\":\{report.issue_count()},\"accepted\":\{report.accepted_media()},\"rejected\":\{report.rejected_media()}}"
}

///|
/// Runs a CLI command. All outputs are one-line JSON and no branch raises.
pub fn run_cli(args : Array[String]) -> String {
  if args.length() == 0 {
    return cli_help()
  }
  let command = args[0].to_lower()
  if command == "help" {
    return cli_help()
  }
  if command == "version" {
    return "{\"ok\":true,\"name\":\"moon-sdp\",\"version\":\"\{library_version()}\",\"iana_snapshot\":\"\{registry_snapshot_date()}\"}"
  }
  if command == "offer-answer" {
    if args.length() != 3 {
      return cli_failure(
        "usage", "offer-answer requires offer and answer SDP arguments",
      )
    }
    return cli_offer_answer(args[1], args[2])
  }
  if args.length() != 2 {
    return cli_failure("usage", "command requires one SDP argument")
  }
  match command {
    "parse" => cli_parse(args[1])
    "validate" => cli_validate(args[1])
    "canonicalize" => cli_canonicalize(args[1])
    "inspect" => cli_inspect(args[1])
    "stats" => cli_stats(args[1])
    "audit" => cli_audit(args[1])
    _ => cli_failure("unknown-command", "unknown command; use help")
  }
}