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

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

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

///|
fn help_message() -> String {
  let message =
    #|Usage: moon run cmd/jqlog -- [options]  [input]
    #|
    #|Options:
    #|  -f, --file    Read input text from a file.
    #|  -h, --help          Show this help message.
    #|
    #|Notes:
    #|  - Input is treated as JSONL/NDJSON with possible non-JSON lines.
    #|  - Non-JSON lines are skipped.
    #|  - If [input] is omitted, stdin is used.
    #|  - If [input] is provided without --file, it is treated as raw text.
    #|
    #|Examples:
    #|  cat logs.ndjson | moon run cmd/jqlog -- '.message'
    #|  moon run cmd/jqlog -- -f output.txt '.a'
  message
}

///|
fn parse_cli(args : ArrayView[String]) -> CliParseResult {
  let mut input_opt : InputSource? = None
  let mut index = 0
  while index < args.length() {
    let arg = args[index]
    if arg == "-h" || arg == "--help" {
      return Help
    }
    if arg == "-f" || arg == "--file" {
      if index + 1 >= args.length() {
        return Error("Missing path after --file")
      }
      if input_opt is Some(_) {
        return Error("Multiple inputs provided")
      }
      input_opt = Some(File(args[index + 1]))
      index = index + 2
      continue
    }
    if arg == "--" {
      index = index + 1
      break
    }
    if arg.has_prefix("-") {
      return Error("Unknown option: \"" + arg + "\"")
    }
    break
  }
  if index >= args.length() {
    return Error("Missing query")
  }
  let query = args[index]
  index = index + 1
  if index < args.length() {
    if input_opt is Some(_) {
      return Error("Multiple inputs provided")
    }
    let inline = args[index]
    if inline == "-" {
      input_opt = Some(Stdin)
    } else {
      input_opt = Some(Inline(inline))
    }
    index = index + 1
  }
  if index < args.length() {
    return Error("Too many arguments")
  }
  let input = match input_opt {
    Some(source) => source
    None => 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[Json]) -> Unit {
  let mut first = true
  for value in results {
    if first {
      first = false
    } else {
      @stdio.stdout.write("\n")
    }
    @stdio.stdout.write(value.stringify())
  }
  if !first {
    @stdio.stdout.write("\n")
  }
}

///|
async fn main {
  let args = @env.args()[1:]
  match parse_cli(args) {
    Help => {
      @stdio.stdout.write(help_message())
      return
    }
    Error(msg) => {
      @stdio.stderr.write("Error: " + msg + "\n")
      @stdio.stderr.write(help_message())
      return
    }
    Run(options) =>
      try {
        let input = read_input(options.input)
        let query = @moonjq.parse(options.query)
        let results = query.eval_logs(input)
        write_results(results)
      } catch {
        err => {
          @stdio.stderr.write("Error: \{err}\n")
          return
        }
      }
  }
}