///|
/// Moonbit_EPUB 命令行工具入口(S8)。
///
/// 子命令:
/// - `build  [--format base64|hex]` 生成 EPUB 并以文本编码输出;
/// - `check  [--json]` 校验结构并输出诊断报告;
/// - `inspect ` 打印书籍结构树。
///
/// 核心库不依赖文件 IO,故 `build` 把二进制以 Base64(默认)或十六进制
/// 输出到标准输出,由 shell 重定向并解码落盘,例如:
/// `moon-epub build minimal | base64 -d > minimal.epub`。

// ---------------------------------------------------------------------------
// 参数解析
// ---------------------------------------------------------------------------

///|
/// 去除程序名与 js 脚本路径,返回有效参数。
///
/// js 后端下 `@env.args()` 形如 `["node", "main.js", ...]`,需跳过前两项;
/// 其他后端仅跳过可执行文件名。
fn argv_of(args : Array[String]) -> Array[String] {
  let mut i = 1
  if args.length() >= 2 && args[1].has_suffix(".js") {
    i = 2
  }
  let out : Array[String] = []
  let mut j = i
  while j < args.length() {
    out.push(args[j])
    j += 1
  }
  out
}

///|
/// 首个不以 `-` 开头的参数(即位置参数)。
fn first_positional(argv : Array[String]) -> String {
  let mut i = 1
  while i < argv.length() {
    if !argv[i].has_prefix("-") {
      return argv[i]
    }
    i += 1
  }
  ""
}

///|
/// 是否存在指定标志。
fn has_flag(argv : Array[String], name : String) -> Bool {
  for arg in argv {
    if arg == name {
      return true
    }
  }
  false
}

///|
/// 取 `--name value` 形式的选项值。
fn option_value(argv : Array[String], name : String) -> String? {
  let mut i = 0
  while i + 1 < argv.length() {
    if argv[i] == name {
      return Some(argv[i + 1])
    }
    i += 1
  }
  None
}

///|
/// 以非零状态结束进程。
///
/// 各后端分别调用平台退出接口,避免使用 panic 造成堆栈噪音;
/// 调用方应先输出人类可读的原因。
fn fail_exit() -> Unit {
  cli_exit(1)
}

///|
/// wasm-gc 后端的进程退出。
#cfg(target="wasm-gc")
fn cli_exit(code : Int) -> Unit = "__moonbit_sys_unstable" "exit"

///|
/// wasm 后端的进程退出。
#cfg(target="wasm")
fn cli_exit(code : Int) -> Unit = "wasi_snapshot_preview1" "proc_exit"

///|
/// native / llvm 后端的进程退出。
#cfg(any(target="native", target="llvm"))
extern "c" fn cli_exit(code : Int) = "exit"

///|
/// js 后端的进程退出:优先调用 `process.exit`,不可用时回退到 panic。
#cfg(target="js")
extern "js" fn cli_exit_js(code : Int) -> Bool =
  #| function(code) {
  #|   if (typeof process !== "undefined" && typeof process.exit === "function") {
  #|     process.exit(code);
  #|     return true;
  #|   }
  #|   return false;
  #| }

///|
/// js 后端的进程退出包装。
#cfg(target="js")
fn cli_exit(code : Int) -> Unit {
  if !cli_exit_js(code) {
    panic()
  }
}

///|
/// 校验报告对应的 CLI 退出码:含错误为 1,否则为 0。
fn exit_code_of(report : @validate.Report) -> Int {
  if report.has_errors() {
    1
  } else {
    0
  }
}

// ---------------------------------------------------------------------------
// 用法
// ---------------------------------------------------------------------------

///|
/// 打印顶层用法说明。
fn print_usage() -> Unit {
  println("moon-epub — EPUB 3 电子书生成引擎")
  println("")
  println("用法:moon-epub  [arguments]")
  println("")
  println("命令:")
  println(
    "  build  [--format base64|hex]  生成 EPUB 并编码输出",
  )
  println(
    "  check  [--json]               校验结构并输出诊断",
  )
  println("  inspect                       打印书籍结构树")
  println("  examples                               列出内置示例")
  println("  help                                   显示本帮助")
  println("")
  println("示例:")
  println("  moon-epub build minimal | base64 -d > minimal.epub")
  println("  moon-epub check multi-chapter --json")
  println("  moon-epub inspect styled")
}

// ---------------------------------------------------------------------------
// 结构树
// ---------------------------------------------------------------------------

///|
/// 打印书籍结构树。
fn render_inspect(book : @model.Book) -> String {
  let out : Array[Char] = []
  let metadata = book.metadata
  out.push_iter("书籍:".iter())
  out.push_iter(metadata.title.iter())
  out.push('\n')
  out.push_iter("语言:".iter())
  out.push_iter(metadata.language.iter())
  out.push('\n')
  out.push_iter("标识符:".iter())
  out.push_iter(metadata.identifier.iter())
  out.push('\n')
  out.push_iter("章节(".iter())
  out.push_iter(book.chapter_count().to_string().iter())
  out.push_iter("):".iter())
  out.push('\n')
  @model.walk_chapters(book.chapters, (depth, chapter) => {
    let mut i = 0
    while i <= depth {
      out.push_iter("  ".iter())
      i += 1
    }
    out.push_iter("• ".iter())
    out.push_iter(chapter.title.iter())
    out.push_iter(" [".iter())
    out.push_iter(chapter.id.iter())
    out.push_iter("]".iter())
    match @cfi.chapter_cfi(book, chapter.id) {
      Some(cfi) => {
        out.push_iter("  ".iter())
        out.push_iter(cfi.render().iter())
      }
      None => ()
    }
    out.push('\n')
  })
  out.push_iter("资源(".iter())
  out.push_iter(book.resources.length().to_string().iter())
  out.push_iter("):".iter())
  out.push('\n')
  for resource in book.resources {
    out.push_iter("  ".iter())
    out.push_iter(resource.href.iter())
    out.push_iter("  ".iter())
    out.push_iter(resource.media_type.iter())
    out.push_iter("  ".iter())
    out.push_iter(resource.data.length().to_string().iter())
    out.push_iter(" B".iter())
    out.push('\n')
  }
  out.push_iter("清单条目:".iter())
  out.push_iter(book.manifest.length().to_string().iter())
  out.push('\n')
  out.push_iter("阅读顺序条目:".iter())
  out.push_iter(book.spine.items.length().to_string().iter())
  out.push('\n')
  String::from_array(out)
}

// ---------------------------------------------------------------------------
// 子命令
// ---------------------------------------------------------------------------

///|
/// 未知示例的提示。
fn unknown_example(name : String) -> Unit {
  println("未知示例:" + name)
  println("可用示例:" + join_names())
  fail_exit()
}

///|
/// 以顿号连接全部示例名称。
fn join_names() -> String {
  let out : Array[Char] = []
  for i, name in @examples.names() {
    if i > 0 {
      out.push_iter("、".iter())
    }
    out.push_iter(name.iter())
  }
  String::from_array(out)
}

///|
/// 为书籍生成与 `build` / `check` 共用的打包选项。
fn package_options_for(
  name : String,
  book : @model.Book,
) -> @ocf.PackageOptions {
  let options = @ocf.PackageOptions::new()
  // 为样式表资源生成指向内容文档的相对链接
  for resource in book.resources {
    if resource.media_type == @spec.mt_css {
      options.add_stylesheet("../" + @model.resource_href(resource))
    }
  }
  if name == "media" {
    options.add_media_overlay(
      "text/ch1.smil",
      "text/ch1.xhtml",
      @examples.media_smil(),
      duration_ms=1200,
    )
  }
  options
}

///|
/// `build`:生成 EPUB 并按指定编码输出。
fn run_build(argv : Array[String]) -> Unit {
  let name = first_positional(argv)
  match @examples.by_name(name) {
    None => unknown_example(name)
    Some(book) => {
      let options = package_options_for(name, book)
      let epub = @ocf.package_book(book, options)
      let format = match option_value(argv, "--format") {
        Some(value) => value
        None => "base64"
      }
      if format == "hex" {
        println(@hex.encode(epub))
      } else {
        println(@base64.encode(epub))
      }
    }
  }
}

///|
/// `check`:校验结构并输出诊断报告。
fn run_check(argv : Array[String]) -> Unit {
  let name = first_positional(argv)
  match @examples.by_name(name) {
    None => unknown_example(name)
    Some(book) => {
      let options = package_options_for(name, book)
      let documents = @ocf.render_documents(book, options)
      let report = @validate.validate_book(book)
      // 校验实际生成的 OPF / nav / NCX 文本与模型是否一致
      report.merge(
        @validate.validate_documents(
          documents.book.metadata,
          documents.book.manifest,
          documents.book.spine,
          documents.opf,
          documents.nav,
          documents.ncx,
        ),
      )
      // 同时检查实际生成的容器布局(mimetype 位置、必需文件等)
      let packaged = @ocf.package_book(book, options)
      report.merge(@validate.validate_package(packaged))
      if has_flag(argv, "--json") {
        println(@validate.render_json(report))
      } else {
        println(@validate.render_text(report))
      }
      if exit_code_of(report) != 0 {
        fail_exit()
      }
    }
  }
}

///|
/// `inspect`:打印书籍结构树。
fn run_inspect(argv : Array[String]) -> Unit {
  let name = first_positional(argv)
  match @examples.by_name(name) {
    None => unknown_example(name)
    Some(book) => println(render_inspect(book))
  }
}

///|
/// `examples`:列出内置示例。
fn run_examples() -> Unit {
  println("内置示例:")
  for name in @examples.names() {
    println("  " + name)
  }
}

// ---------------------------------------------------------------------------
// 入口
// ---------------------------------------------------------------------------

///|
/// 程序入口。
fn main {
  let argv = argv_of(@env.args())
  let command = if argv.length() > 0 { argv[0] } else { "" }
  match command {
    "" | "help" | "--help" | "-h" => print_usage()
    "build" => run_build(argv)
    "check" => run_check(argv)
    "inspect" => run_inspect(argv)
    "examples" | "list" => run_examples()
    _ => {
      println("未知命令:" + command)
      println("")
      print_usage()
      fail_exit()
    }
  }
}