// `rich`: port of Textualize's rich-cli. The rendering lives in
// `bobzhang/rich/cli`; this executable does the IO (arguments, files,
// standard input/output) with `moonbitlang/async`, on native and wasm.

///|
async fn main {
  let code = run(@env.args()[1:].to_owned()) catch {
    e => {
      @stdio.stderr.write("rich: \{e}\n")
      1
    }
  }
  @sys.exit(code)
}

///|
/// Python's `str(OSError)` for `open(path)`, e.g. `[Errno 2] No such file or
/// directory: 'x.py'`.
fn os_error_message(e : Error, path : String) -> String {
  match e {
    @os_error.OSError(errno, ..) as err => {
      let code = if err.is_ENOENT() {
        2
      } else if err.is_EACCES() {
        13
      } else {
        errno
      }
      "[Errno \{code}] \{strerror(err)}: \{@richlib.py_repr(path)}"
    }
    _ => e.to_string()
  }
}

///|
/// The system message of an `OSError` ("No such file or directory"): its
/// `to_string` renders `OSError("context: message")`.
fn strerror(e : @os_error.OSError) -> String {
  let s = e.to_string()
  let msg = match s.rev_find(": ") {
    Some(i) => s.view(start_offset=i + 2).to_owned()
    None => s
  }
  if msg.has_suffix("\")") {
    msg.view(end_offset=msg.length() - 2).to_owned()
  } else {
    msg
  }
}

///|
/// Read a file as UTF-8, replacing invalid bytes (like `open(path,
/// encoding="utf8", errors="replace")`).
async fn read_text(path : String) -> Result[String, String] {
  let data = @fs.read_file(path) catch {
    e => return Err(os_error_message(e, path))
  }
  Ok(@utf8.decode_lossy(data.binary()))
}

///|
async fn run(args : Array[String]) -> Int32 {
  let stdout_isatty = @term.isatty(1)
  let stderr_isatty = @term.isatty(2)
  let command = @cli.parse_args(args)
  let input = match command {
    Run(options) =>
      match options.input() {
        NoInput => None
        Stdin => Some(Ok(@utf8.decode_lossy(@stdio.stdin.read_all().binary())))
        File(path) => Some(read_text(path))
      }
    _ => None
  }
  let output = @cli.execute(command, input?, stdout_isatty~, stderr_isatty~)
  if output.stdout != "" {
    @stdio.stdout.write(output.stdout)
  }
  if output.stderr != "" {
    @stdio.stderr.write(output.stderr)
  }
  if output.pager is Some(text) {
    @richlib.SystemPager::new().show(text)
  }
  for exported in output.exports {
    @fs.write_file(
      exported.path,
      exported.contents,
      create_mode=CreateOrTruncate,
    ) catch {
      e => {
        @stdio.stderr.write(
          @cli.error_text(
            "failed to save \{exported.format}",
            error=os_error_message(e, exported.path),
            isatty=stderr_isatty,
          ),
        )
        return 255
      }
    }
  }
  output.exit_code
}