///|
fn option(
  args : Array[String],
  name : String,
) -> String raise @types.EngineError {
  for i, arg in args {
    if arg == name && i + 1 < args.length() {
      return args[i + 1]
    }
  }
  raise @types.fail("E_CLI_ARGUMENT", "missing " + name)
}

///|
fn run(args : Array[String]) -> Unit raise Error {
  if args.length() < 3 {
    raise @types.fail(
      "E_CLI_USAGE", "usage: moondmn feel  [--context file] [--now ISO-datetime] | eval  --decision name --input file | lint  | tck  [--json out]",
    )
  }
  let clock = if args.contains("--now") {
    let fixed = @types.parse_datetime(option(args, "--now"))
    Some(fn() { fixed })
  } else {
    None
  }
  match args[1] {
    "lint" =>
      println(
        @model.lint(@lib.parse_model(@fs.read_file_to_string(args[2]))).stringify(
          indent=2,
        ),
      )
    "tck" => {
      let report = @tck.run(args[2]).stringify(indent=2)
      if args.contains("--json") {
        @fs.write_string_to_file(option(args, "--json"), report)
      } else {
        println(report)
      }
    }
    "feel" => {
      let context = if args.contains("--context") {
        @fs.read_file_to_string(option(args, "--context"))
      } else {
        "{}"
      }
      println(@lib.feel_eval(args[2], context~, clock~))
    }
    "eval" => {
      let model = @lib.parse_model(@fs.read_file_to_string(args[2]))
      println(
        @lib.evaluate_decision(
          model,
          option(args, "--decision"),
          @fs.read_file_to_string(option(args, "--input")),
          clock~,
        ),
      )
    }
    _ => raise @types.fail("E_CLI_USAGE", "unknown command")
  }
}

///|
fn main {
  run(@env.args()) catch {
    @types.Failure(code, line, column, message) => {
      println(
        Json::object(
          Map([
            ("error", Json(code)),
            ("line", Json(line)),
            ("column", Json(column)),
            ("message", Json(message)),
          ]),
        ).stringify(),
      )
      @sys.exit(1)
    }
    e => {
      println(
        Json::object(
          Map([("error", Json("E_IO")), ("message", Json(e.to_string()))]),
        ).stringify(),
      )
      @sys.exit(1)
    }
  }
}