///|
#cfg(target="wasm")
fn runtime_exit(code : Int) -> Unit = "wasi_snapshot_preview1" "proc_exit"

///|
#cfg(target="native")
extern "c" fn runtime_exit(code : Int) -> Unit = "exit"

///|
async fn write_raw_cli_stdout(output : String) -> Unit {
  if output != "" {
    if output.strip_suffix("\n") is Some(_) {
      @stdio.stdout.write(output)
    } else {
      @stdio.stdout.write(output + "\n")
    }
  }
}

///|
async fn write_raw_cli_stderr(output : String) -> Unit {
  if output != "" {
    if output.strip_suffix("\n") is Some(_) {
      @stdio.stderr.write(output)
    } else {
      @stdio.stderr.write(output + "\n")
    }
  }
}

///|
#warnings("-alert_internal")
fn cli_errno_reason(errno : Int) -> String {
  @os_error.errno_to_string(errno)
}

///|
async fn write_cli_stdout(output : String) -> Unit {
  write_raw_cli_stdout(output) catch {
    @os_error.OSError(errno, ..) =>
      raise @cli.CliError::output_error(
        "standard output",
        cli_errno_reason(errno),
      )
    err => raise err
  }
}

///|
async fn exit_with_cli_error(error : @cli.CliError, output_json : Bool) -> Unit {
  let exit_code = error.exit_code()
  let rendered = if output_json { error.render_json() } else { "\{error}" }
  write_raw_cli_stderr(rendered) catch {
    @os_error.OSError(_) => runtime_exit(7)
    err => raise err
  }
  runtime_exit(exit_code)
}

///|
async fn run_cli(argv : Array[String]) -> Unit {
  let command = @cli.parse_cli_command(argv, env=@env.get_env_vars())
  match command {
    Scan(options) => @cli.run_scan_command(options)
    Lint(options) => @cli.run_lint_command(options)
    DocsList | DocsShow(_) =>
      write_cli_stdout(@cli.render_docs_command(command))
    DumpImpl(..) | DumpExpr(..) =>
      write_cli_stdout(@cli.render_dump_command(command))
  }
}

///|
async fn main {
  let argv = @cli.runtime_cli_args(@env.args())
  let output_json = @cli.output_json_requested(argv)
  run_cli(argv) catch {
    @cli.CliError::Internal(..) as err => exit_with_cli_error(err, output_json)
    @cli.CliError::Usage(..) as err => exit_with_cli_error(err, output_json)
    @cli.CliError::DumpInput(..) as err => exit_with_cli_error(err, output_json)
    @cli.CliError::RuleSource(..) as err =>
      exit_with_cli_error(err, output_json)
    @cli.CliError::RuleContent(..) as err =>
      exit_with_cli_error(err, output_json)
    @cli.CliError::ScanInput(..) as err => exit_with_cli_error(err, output_json)
    @cli.CliError::Output(..) as err => exit_with_cli_error(err, output_json)
    err => exit_with_cli_error(@cli.CliError::internal("\{err}"), output_json)
  }
}