///|
/// 命令行选项。
struct CliOptions {
  mut show_version : Bool
  mut show_help : Bool
  mut file : String?
  mut start : Int?
  mut end : Int?
  mut content : String?
  mut old : String?
  mut viewed_code_id : String?
  mut output : String?
}

///|
/// 将 BeError 转换为带翻译前缀的消息。
fn format_error(err : @error.BeError) -> String {
  match err {
    @error.BeError::InvalidFormat(msg) =>
      @common.t("error.InvalidFormat") + ": " + msg
    @error.BeError::InvalidArgument(msg) =>
      @common.t("error.InvalidArgument") + ": " + msg
    @error.BeError::FileNotFound(msg) =>
      @common.t("error.FileNotFound") + ": " + msg
    @error.BeError::NotARegularFile(msg) =>
      @common.t("error.NotARegularFile") + ": " + msg
    @error.BeError::PathError(msg) => @common.t("error.PathError") + ": " + msg
    @error.BeError::ReadError(msg) => @common.t("error.ReadError") + ": " + msg
    @error.BeError::WriteError(msg) =>
      @common.t("error.WriteError") + ": " + msg
    @error.BeError::IOError(msg) => @common.t("error.IOError") + ": " + msg
  }
}

///|
/// 解析整数参数,失败返回 None。
fn parse_int_arg(s : String) -> Int? {
  Some(@strconv.from_str(s) catch { _ => return None })
}

///|
/// 输出文本到 stdout 或指定文件。
fn emit_output(text : String, output : String?) -> Int {
  match output {
    None => {
      println(text)
      0
    }
    Some(path) =>
      try {
        @fs.write_string_to_file(path, text)
        0
      } catch {
        IOError(msg) => {
          println(@common.t("error.WriteError") + ": " + msg)
          2
        }
      }
  }
}

///|
/// 返回帮助文本。
fn help_text() -> String {
  [
    @common.t("cli.help.usage"),
    "",
    "Commands:",
    "  read        " + @common.t("cli.command.read"),
    "  replace     " + @common.t("cli.command.replace"),
    "  insert      " + @common.t("cli.command.insert"),
    "  delete      " + @common.t("cli.command.delete"),
    "  write       " + @common.t("cli.command.write"),
    "  balance     " + @common.t("cli.command.balance"),
    "  func-range  " + @common.t("cli.command.func-range"),
    "  tag-range   " + @common.t("cli.command.tag-range"),
    "",
    @common.t("cli.help.global_flags") + ":",
    "  --lang ",
    "  --no-prefix",
    "  --version   " + @common.t("cli.help.version"),
    "  --help",
    "",
    "Common flags:",
    "  --file ",
    "  -s, --start ",
    "  -e, --end ",
    "  -c, --content ",
    "  --old ",
    "  --viewed_code_id ",
    "  --preview",
    "  --brief",
    "  --output ",
  ].join("\n")
}

///|
/// 返回版本文本。
fn version_text() -> String {
  @common.t("cli.help.version") + " 0.1.1"
}

///|
/// 构造 read 工具的输入字符串。
fn build_range_input(file : String, start : Int?, end : Int?) -> String {
  match start {
    None => file
    Some(s) =>
      match end {
        None => "\{file}:\{s}"
        Some(e) => "\{file}:\{s}-\{e}"
      }
  }
}

///|
/// 解析命令行参数。返回 (选项, 子命令, 解析错误)。
fn parse_args(args : Array[String]) -> (CliOptions, String?, String?) {
  let opts : CliOptions = {
    show_version: false,
    show_help: false,
    file: None,
    start: None,
    end: None,
    content: None,
    old: None,
    viewed_code_id: None,
    output: None,
  }
  let mut command : String? = None
  let mut error : String? = None
  let mut i = 1
  while i < args.length() {
    let arg = args[i]
    if arg == "--lang" {
      i += 1
      if i >= args.length() {
        error = Some("Missing value for --lang")
        break
      }
      @common.set_language(args[i])
    } else if arg == "--no-prefix" {
      // 保留兼容性,忽略
    } else if arg == "--version" {
      opts.show_version = true
    } else if arg == "--help" {
      opts.show_help = true
    } else if arg == "--file" {
      i += 1
      if i >= args.length() {
        error = Some("Missing value for --file")
        break
      }
      opts.file = Some(args[i])
    } else if arg == "-s" || arg == "--start" {
      i += 1
      if i >= args.length() {
        error = Some("Missing value for --start")
        break
      }
      match parse_int_arg(args[i]) {
        Some(n) => opts.start = Some(n)
        None => {
          error = Some("Invalid integer for --start: " + args[i])
          break
        }
      }
    } else if arg == "-e" || arg == "--end" {
      i += 1
      if i >= args.length() {
        error = Some("Missing value for --end")
        break
      }
      match parse_int_arg(args[i]) {
        Some(n) => opts.end = Some(n)
        None => {
          error = Some("Invalid integer for --end: " + args[i])
          break
        }
      }
    } else if arg == "-c" || arg == "--content" {
      i += 1
      if i >= args.length() {
        error = Some("Missing value for --content")
        break
      }
      opts.content = Some(args[i])
    } else if arg == "--old" {
      i += 1
      if i >= args.length() {
        error = Some("Missing value for --old")
        break
      }
      opts.old = Some(args[i])
    } else if arg == "--viewed_code_id" {
      i += 1
      if i >= args.length() {
        error = Some("Missing value for --viewed_code_id")
        break
      }
      opts.viewed_code_id = Some(args[i])
    } else if arg == "--preview" {
      // 保留标志位,当前仅解析
    } else if arg == "--brief" {
      // 保留标志位,当前仅解析
    } else if arg == "--output" {
      i += 1
      if i >= args.length() {
        error = Some("Missing value for --output")
        break
      }
      opts.output = Some(args[i])
    } else if arg.has_prefix("-") {
      error = Some("Unknown flag: " + arg)
      break
    } else if command is None {
      command = Some(arg)
    } else {
      error = Some("Unexpected argument: " + arg)
      break
    }
    i += 1
  }
  (opts, command, error)
}

///|
/// 解析 CLI 参数并执行,返回退出码。
pub fn run_cli(args : Array[String]) -> Int {
  let (opts, command, parse_error) = parse_args(args)
  if parse_error is Some(msg) {
    println(@common.t("error.InvalidFormat") + ": " + msg)
    return 1
  }
  if opts.show_version {
    return emit_output(version_text(), opts.output)
  }
  if opts.show_help {
    return emit_output(help_text(), opts.output)
  }
  let cmd = match command {
    Some(c) => c
    None => {
      let _ = emit_output(help_text(), opts.output)
      return 1
    }
  }
  let file = match opts.file {
    Some(f) => f
    None => {
      println(@common.t("error.InvalidFormat") + ": --file is required")
      return 1
    }
  }
  let content = match opts.content {
    Some(v) => v
    None => ""
  }
  let viewed_code_id = match opts.viewed_code_id {
    Some(v) => v
    None => ""
  }
  let old = match opts.old {
    Some(v) => v
    None => ""
  }
  match cmd {
    "read" => {
      let input = build_range_input(file, opts.start, opts.end)
      try {
        let result = @read.be_read(input)
        emit_output(result.range.content, opts.output)
      } catch {
        err => {
          println(format_error(err))
          2
        }
      }
    }
    "replace" => {
      guard opts.start is Some(start_line) else {
        println(@common.t("error.InvalidFormat") + ": --start is required")
        return 1
      }
      guard opts.end is Some(end_line) else {
        println(@common.t("error.InvalidFormat") + ": --end is required")
        return 1
      }
      guard opts.content is Some(_) else {
        println(@common.t("error.InvalidFormat") + ": --content is required")
        return 1
      }
      try {
        let result = @edit.be_replace(
          file,
          start_line,
          end_line,
          content,
          viewed_code_id~,
          old~,
        )
        let out = if result.preview && !result.brief && result.diff.length() > 0 {
          result.status + "\n" + result.diff
        } else {
          "OK"
        }
        emit_output(out, opts.output)
      } catch {
        err => {
          println(format_error(err))
          2
        }
      }
    }
    "insert" => {
      guard opts.content is Some(_) else {
        println(@common.t("error.InvalidFormat") + ": --content is required")
        return 1
      }
      let after_line = match opts.start {
        Some(s) => s
        None =>
          match opts.end {
            Some(e) => e
            None => 0
          }
      }
      try {
        let result = @edit.be_insert(file, content, after_line~)
        let out = if result.preview && !result.brief && result.diff.length() > 0 {
          result.status + "\n" + result.diff
        } else {
          "OK"
        }
        emit_output(out, opts.output)
      } catch {
        err => {
          println(format_error(err))
          2
        }
      }
    }
    "delete" => {
      guard opts.start is Some(_) else {
        println(@common.t("error.InvalidFormat") + ": --start is required")
        return 1
      }
      guard opts.end is Some(_) else {
        println(@common.t("error.InvalidFormat") + ": --end is required")
        return 1
      }
      let s = match opts.start {
        Some(v) => v
        None => 0
      }
      let e = match opts.end {
        Some(v) => v
        None => 0
      }
      try {
        let result = @edit.be_delete(file, start=s, end=e)
        let out = if result.preview && !result.brief && result.diff.length() > 0 {
          result.status + "\n" + result.diff
        } else {
          "OK"
        }
        emit_output(out, opts.output)
      } catch {
        err => {
          println(format_error(err))
          2
        }
      }
    }
    "write" => {
      guard opts.content is Some(_) else {
        println(@common.t("error.InvalidFormat") + ": --content is required")
        return 1
      }
      try {
        let input : Json = { "file": file, "content": content }
        let result = @write.be_write(input.stringify())
        let mut failed : String? = None
        for r in result.results {
          if r != "ok" {
            failed = Some(r)
            break
          }
        }
        match failed {
          Some(msg) => {
            let _ = emit_output(msg, opts.output)
            2
          }
          None => emit_output("OK", opts.output)
        }
      } catch {
        err => {
          println(format_error(err))
          2
        }
      }
    }
    "balance" =>
      try {
        let result = @check.be_balance(file, verbose=true)
        emit_output(result.to_json().stringify(), opts.output)
      } catch {
        err => {
          println(format_error(err))
          2
        }
      }
    "func-range" => {
      guard opts.start is Some(line) else {
        println(@common.t("error.InvalidFormat") + ": --start is required")
        return 1
      }
      try {
        let result = @check.be_func_range(file, line)
        emit_output(result.to_json().stringify(), opts.output)
      } catch {
        err => {
          println(format_error(err))
          2
        }
      }
    }
    "tag-range" => {
      guard opts.start is Some(line) else {
        println(@common.t("error.InvalidFormat") + ": --start is required")
        return 1
      }
      try {
        let result = @check.be_tag_range(file, line)
        emit_output(result.to_json().stringify(), opts.output)
      } catch {
        err => {
          println(format_error(err))
          2
        }
      }
    }
    _ => {
      println(@common.t("error.UnknownTool") + ": " + cmd)
      1
    }
  }
}