///|
/// status 的库状态报表(jumbit 扩展,上游无):计算与渲染分离(渲染可 wbtest 直断字节)。
/// existing 仅在 --check 时为 Some(O(N) 逐路径存在性扫描,默认快查不做);
/// 无数据文件视为空库(file_size 0 / entries 0 / format None)。
/// format = 实际加载格式的标识(规格 docs/spec-plaintext-edit.md §3.4):
/// "plaintext"(db.txt,主格式)/ "binary"(legacy db.zo,只读不迁移)/ None(无库)——
/// 二进制内部版本号不外泄(诊断价值由「数据文件损坏」路径承担)
struct DbStatus {
  data_file : String
  file_size : Int
  format : String?
  entries : Int
  notes : Int
  /// _JB_MAXAGE 现值(config 解析产物为 Double)
  maxage : Double
  existing : Int?
} derive(Debug)

///|
fn render_status_human(r : DbStatus) -> Array[String] {
  let fmt = match r.format {
    Some(v) => v
    None => "-"
  }
  let lines = [
    "data file      \{r.data_file}",
    "file size      \{r.file_size} bytes",
    "format         \{fmt}",
    "entries        \{r.entries}",
    "notes          \{r.notes}",
    "aging threshold \{r.maxage}",
  ]
  match r.existing {
    Some(k) => lines.push("existing        \{k}/\{r.entries}")
    None => ()
  }
  lines
}

///|
/// 单行 JSON 对象(键序固定,--json 纪律同 query --json);existing 仅 --check 出现。
/// format_version 键 0.1.2 起更名 format、值改格式标识字符串(breaking,
/// CHANGELOG 注明)
fn render_status_json(r : DbStatus) -> String {
  let fmt = match r.format {
    Some(v) => "\"\{v}\""
    None => "null"
  }
  let existing = match r.existing {
    Some(k) => ",\"existing\":\{k}"
    None => ""
  }
  "{\"data_file\":\"\{json_escape(r.data_file)}\",\"file_size\":\{r.file_size},\"format\":\{fmt},\"entries\":\{r.entries},\"notes\":\{r.notes},\"maxage\":\{r.maxage}\{existing}}"
}

///|
/// 加载库并汇总条目/标注(统一走 platform.load 单一加载路径;legacy db.zo
/// 被标 dirty 但 status 只读不 save,不触发迁移)。返回 None = 加载失败
/// (stderr 已报)
async fn load_status_dirs(
  data_dir : String,
  errs : Array[String],
) -> Array[@core.Dir]? {
  let store = match @platform.load(data_dir) {
    Ok(s) => s
    Err(Io(msg)) => {
      err_line(errs, "jumbit: could not read data file: \{msg}")
      return None
    }
    Err(e) => {
      err_line(errs, "jumbit: corrupted data file: \{e.message()}")
      return None
    }
  }
  Some(store.database().all())
}

///|
async fn run_status(
  sopts : @cli.StatusOptions,
  getenv : (String) -> String?,
  errs : Array[String],
) -> Int {
  let data_dir = match cfg(@config.data_dir(getenv), errs) {
    Some(data_dir) => data_dir
    None => return 1
  }
  let maxage = match cfg(@config.maxage(getenv), errs) {
    Some(maxage) => maxage
    None => return 1
  }
  let dirs = match load_status_dirs(data_dir, errs) {
    Some(dirs) => dirs
    None => return 1
  }
  // 数据文件事实:db.txt 主格式优先,回落 legacy db.zo;均无 → 主路径 + 零值
  let txt_path = data_dir + "/db.txt"
  let zo_path = data_dir + "/db.zo"
  let (data_file, format) = if @afs.exists(txt_path) {
    (txt_path, Some("plaintext"))
  } else if @afs.exists(zo_path) {
    (zo_path, Some("binary"))
  } else {
    (txt_path, None)
  }
  let file_size = if @afs.exists(data_file) {
    let data = @afs.read_file(data_file) catch {
      err => {
        err_line(
          errs,
          "jumbit: could not read data file: \{@platform.os_error_message(err)}",
        )
        return 1
      }
    }
    data.binary().length()
  } else {
    0
  }
  let mut notes = 0
  for d in dirs {
    if d.note is Some(_) {
      notes += 1
    }
  }
  let existing = if sopts.check {
    let mut k = 0
    for d in dirs {
      if @afs.exists(d.path) {
        k += 1
      }
    }
    Some(k)
  } else {
    None
  }
  let report : DbStatus = {
    data_file,
    file_size,
    format,
    entries: dirs.length(),
    notes,
    maxage,
    existing,
  }
  if sopts.json {
    println(render_status_json(report))
  } else {
    for line in render_status_human(report) {
      println(line)
    }
  }
  0
}