// moonkv 的 dbcli 适配器：让 benches/python/bench.py 用同一个协议驱动它。
//
// 存在理由：BENCHMARK.md §5.4 一直写着「没有和 moonkv 对比」。要做这个对比，
// 两边必须跑**同一套脚本、同一个 workload、同一种协议**，否则量出来的是
// 「协议开销」而不是「引擎差异」。
//
// 刻意不做的两件事：
// - **不实现 `sync`**。moonkv 0.1.3 的 put 是 fopen(ab)+fwrite+fflush+fclose，
//   没有 fsync —— 它给不了「断电不丢」的保证，所以也没有可开关的持久化级别。
//   对比只能在「都不 fsync」这一档做，这一条写进 BENCHMARK。
// - **不实现组提交**。moonkv 的 API 没有批量接口，一次 put 就是一次落盘调用。
//
// 错误一律当作「没取到」：`get` 出错返回 null，`put` 出错返回 {"ok":false}。
// 基准脚本只看成功路径，把 KeyNotFound 和 IOError 分开在这里没有意义 ——
// 但这也意味着这个适配器**不能**用来判断 moonkv 的正确性。

extern "C" fn c_getchar() -> Int = "getchar"

extern "C" fn c_fflush(stream : UInt64) -> Int = "fflush"

///|
fn read_line() -> String? {
  let buf = Buffer::Buffer()
  let mut got_any = false
  while true {
    let c = c_getchar()
    if c < 0 {
      break
    }
    got_any = true
    if c == 10 {
      break
    }
    if c == 13 {
      continue
    }
    buf.write_byte(c.to_byte())
  }
  if got_any {
    Some(@utf8.decode_lossy(buf.to_bytes()))
  } else {
    None
  }
}

///|
fn esc(s : String) -> String {
  let buf = Buffer::Buffer()
  for c in s {
    match c {
      '"' => buf.write_string("\\\"")
      '\\' => buf.write_string("\\\\")
      '\n' => buf.write_string("\\n")
      '\r' => buf.write_string("\\r")
      '\t' => buf.write_string("\\t")
      _ => buf.write_char(c)
    }
  }
  buf.to_string()
}

///|
fn jget(j : Json, key : String) -> Json? {
  match j {
    Object(obj) => obj.get(key)
    _ => None
  }
}

///|
fn as_str(j : Json) -> String? {
  match j {
    String(s) => Some(s)
    _ => None
  }
}

///|
fn as_arr(j : Json) -> Array[Json]? {
  match j {
    Array(a) => Some(a)
    _ => None
  }
}

///|
fn fstr(j : Json, key : String) -> String {
  match jget(j, key) {
    Some(v) =>
      match as_str(v) {
        Some(s) => s
        None => ""
      }
    None => ""
  }
}

///|
fn ok_unit() -> String {
  "{\"ok\":true}"
}

///|
fn err(msg : String) -> String {
  "{\"ok\":false,\"error\":\"\{esc(msg)}\"}"
}

///|
/// `get` 在 key 不存在时抛 KeyNotFound。基准只关心「有没有取到」，
/// 所以这里统一折成 Option；IO 错误也走同一条路（这个适配器不判正确性）。
fn get_opt(db : @moonkv.DB, k : String) -> Bytes? {
  let v = db.get(k) catch {
    _ => return None
  }
  Some(v)
}

///|
fn ok_value(v : Bytes?) -> String {
  match v {
    Some(b) => "{\"ok\":true,\"value\":\"\{esc(@moonkv.bytes_to_string(b))}\"}"
    None => "{\"ok\":true,\"value\":null}"
  }
}

///|
fn put_one(db : @moonkv.DB, k : String, v : String) -> String {
  db.put(k, @moonkv.string_to_bytes(v)) catch {
    _ => return err("put failed")
  }
  ok_unit()
}

///|
fn put_many(db : @moonkv.DB, items : Array[Json]) -> String {
  for item in items {
    match as_arr(item) {
      Some(pair) =>
        if pair.length() >= 2 {
          let k = match as_str(pair[0]) {
            Some(s) => s
            None => ""
          }
          let v = match as_str(pair[1]) {
            Some(s) => s
            None => ""
          }
          db.put(k, @moonkv.string_to_bytes(v)) catch {
            _ => return err("put failed")
          }
        }
      None => ()
    }
  }
  ok_unit()
}

///|
fn scan_range(db : @moonkv.DB, start : String, end : String) -> String {
  let rows = db.scan_range(start, end, 500) catch {
    _ => return err("scan failed")
  }
  let buf = Buffer::Buffer()
  buf.write_string("{\"ok\":true,\"items\":[")
  let mut first = true
  for row in rows {
    if !first {
      buf.write_string(",")
    }
    first = false
    buf.write_string(
      "[\"\{esc(row.0)}\",\"\{esc(@moonkv.bytes_to_string(row.1))}\"]",
    )
  }
  buf.write_string("]}")
  buf.to_string()
}

///|
fn handle_line(db : @moonkv.DB, line : String) -> String {
  let req = @json.parse(line) catch {
    _ => return err("bad json")
  }
  match fstr(req, "op") {
    "put" => put_one(db, fstr(req, "k"), fstr(req, "v"))
    "putmany" =>
      put_many(
        db,
        match jget(req, "items") {
          Some(v) =>
            match as_arr(v) {
              Some(a) => a
              None => []
            }
          None => []
        },
      )
    "get" => ok_value(get_opt(db, fstr(req, "k")))
    "scan" => scan_range(db, fstr(req, "start"), fstr(req, "end"))
    // moonkv 没有 memtable，`flush` 是空操作
    "flush" => ok_unit()
    "stats" => "{\"ok\":true,\"keys\":\{db.key_count()}}"
    "close" => {
      db.close()
      ok_unit()
    }
    op => err("unknown op: \{op}")
  }
}

///|
fn main {
  // 优先读 MOONLSM_DATA：bench.py 通过 dbcli_launch.child_env 只设这一个
  // 变量，适配器跟着它走就不用改驱动脚本。
  let dir = match @env.get_env_var("MOONLSM_DATA") {
    Some(d) => d
    None =>
      match @env.get_env_var("MOONKV_DATA") {
        Some(d) => d
        None => ".moonkv_data"
      }
  }
  // 64 MiB 的段大小：与 moonlsm 的 SST 同量级，免得「段滚得太勤」
  // 变成差异来源。这是 open 参数，不属于 workload。
  let db = @moonkv.DB::open(dir, 64 * 1024 * 1024) catch {
    _ => {
      println(err("open failed"))
      return
    }
  }
  while true {
    match read_line() {
      None => break
      Some(line) => {
        if line.length() == 0 {
          continue
        }
        println(handle_line(db, line))
        ignore(c_fflush(0UL))
      }
    }
  }
}
