///|
priv enum InputSource {
  Stdin
  File(String)
  Inline(String)
}

///|
priv struct CliOptions {
  query : String
  input : InputSource
}

///|
priv enum CliParseResult {
  Error(String)
  Run(CliOptions)
}

///|
fn jqlog_command() -> @argparse.Command {
  Command(
    "jqlog",
    about="Run jq filters over JSON Lines input, skipping non-JSON lines.",
    options=[
      OptionArg("file", short='f', about="Read input text from a file.", conflicts_with=[
        "input",
      ]),
    ],
    positionals=[
      PositionArg(
        "query",
        about="The jq filter to evaluate.",
        num_args=@argparse.ValueRange::single(),
      ),
      PositionArg(
        "input",
        about="Input text; omit or use - to read stdin.",
        num_args=ValueRange(lower=0, upper=1),
        allow_hyphen_values=true,
        conflicts_with=["file"],
      ),
    ],
    disable_help_subcommand=true,
  )
}

///|
fn parse_cli(args : ArrayView[String]) -> CliParseResult {
  let matches = jqlog_command().parse(argv=args, env=Map([])) catch {
    err => return Error(err.to_string())
  }
  let query = matches.values.get("query").unwrap()[0]
  let input = match matches.values {
    { "file": [path], .. } => File(path)
    { "input": ["-"], .. } => Stdin
    { "input": [text], .. } => Inline(text)
    _ => Stdin
  }
  Run({ query, input, })
}

///|
async fn read_input(source : InputSource) -> String {
  match source {
    Stdin => @stdio.stdin.read_all().text()
    File(path) => @fs.read_file_to_string(path)
    Inline(text) => text
  }
}

///|
async fn write_results(results : Iter[Result[Json, @moonjq.EvalError]]) -> Unit {
  for result in results {
    let value = match result {
      Ok(v) => v
      Err(err) => raise err
    }
    @stdio.stdout.write(value.stringify())
    @stdio.stdout.write("\n")
  }
}

///|
async fn main {
  let args = @env.args()[1:]
  match parse_cli(args) {
    Error(msg) => {
      @stdio.stderr.write(msg)
      if !msg.has_suffix("\n") {
        @stdio.stderr.write("\n")
      }
      @sys.exit(2)
      return
    }
    Run(options) => {
      let query = @moonjq.parse(options.query) catch {
        err => {
          @stdio.stderr.write("Error: \{err}\n")
          @sys.exit(3)
          return
        }
      }
      try {
        let input = read_input(options.input)
        write_results(query.eval_logs(input))
      } catch {
        err => {
          @stdio.stderr.write("Error: \{err}\n")
          @sys.exit(5)
          return
        }
      }
    }
  }
}