///|
/// 用户组件命令组(docs/11-user-components.md)。
///
/// 引擎只提供注册功能:compile(验收+注册)/ describe / delete /
/// library 快照协议(物理持久化归宿主)/ export+import(MCF 专有格式)。
/// 声明来源不区分 Agent(自然语言翻译)与人类(Studio 沉淀)。

///|

///|
/// 公共注册管线:解析(含内置实例化冒烟)→ 注册(compile 与 import 同一验收)
fn compile_and_register(
  p : @core.Project,
  text : String,
) -> Result[@core.UserComponentDef, String] {
  match @decl.parse_user_component(text) {
    Err(error) => Err(error)
    Ok(def) => {
      p.user_components.set(def.id, def)
      Ok(def)
    }
  }
}

///|
fn cmd_component_compile(p : @core.Project, parts : Array[String]) -> String {
  if parts.length() < 2 {
    return cli_err("usage: component-compile-b64 ")
  }
  match decode_mbt_b64(parts[1]) {
    Err(error) => cli_err(error)
    Ok(text) =>
      match compile_and_register(p, text) {
        Err(error) => cli_err(error)
        Ok(def) => {
          let ps : Array[String] = []
          for param in def.params.iter() {
            ps.push(
              "{\"name\":\"\{@core.json_escape(param.name)}\",\"kind\":\"\{@core.json_escape(param.kind)}\",\"default\":\"\{@core.json_escape(param.default_value)}\"}",
            )
          }
          let vs : Array[String] = []
          for v in def.variants.keys() {
            vs.push("\"\{@core.json_escape(v)}\"")
          }
          "{\"ok\":true,\"op\":\"component-compile\",\"component\":\"\{@core.json_escape(def.id)}\",\"params\":[\{join_arr(ps)}],\"variants\":[\{join_arr(vs)}],\"source_b64\":\"\{encode_mbt_b64(def.source)}\"}"
        }
      }
  }
}

///|
/// MCF 导出:组件 → 专有格式(对外唯一形态;mbt.md 不出引擎)
fn cmd_component_export(p : @core.Project, parts : Array[String]) -> String {
  if parts.length() < 2 {
    return cli_err("usage: component-export ")
  }
  let id = parts[1]
  match p.user_components.get(id) {
    None => cli_err("unknown_component:" + id)
    Some(def) =>
      // header 构造收敛到 core(CLI/MCP 同一 schema)
      match @core.mcf_pack(@core.mcf_header_json(def), def.source) {
        Err(error) => cli_err(error)
        Ok(bytes) =>
          "{\"ok\":true,\"op\":\"component-export\",\"component\":\"\{@core.json_escape(id)}\",\"mcf_b64\":\"\{@base64.encode(bytes)}\"}"
      }
  }
}

///|
/// MCF 导入:严格校验(magic/版本/双 CRC/指纹)→ 解包 → 重跑完整注册管线
fn cmd_component_import(p : @core.Project, parts : Array[String]) -> String {
  if parts.length() < 2 {
    return cli_err("usage: component-import ")
  }
  let bytes = @base64.decode(parts[1]) catch {
    _ => return cli_err("mcf_transport_invalid_base64")
  }
  match @core.mcf_unpack(bytes) {
    Err(error) => cli_err(error)
    Ok((header_json, source)) =>
      match compile_and_register(p, source) {
        Err(error) => cli_err(error)
        Ok(def) =>
          // header_json 由 mcf_header_json 产出,已是合法 JSON 对象文本:
          // 原样嵌入(再转义会破坏结构),收尾 } 是信封闭合(F51)
          "{\"ok\":true,\"op\":\"component-import\",\"component\":\"\{@core.json_escape(def.id)}\",\"header\":\{header_json}}"
      }
  }
}

///|
fn cmd_component_describe(p : @core.Project, parts : Array[String]) -> String {
  if parts.length() < 2 {
    return cli_err("usage: component-describe ")
  }
  let id = parts[1]
  match p.user_components.get(id) {
    None => cli_err("unknown_component:" + id)
    Some(def) => {
      let ps : Array[String] = []
      for param in def.params.iter() {
        ps.push(
          "{\"name\":\"\{@core.json_escape(param.name)}\",\"kind\":\"\{@core.json_escape(param.kind)}\",\"default\":\"\{@core.json_escape(param.default_value)}\"}",
        )
      }
      let vs : Array[String] = []
      for v in def.variants.keys() {
        vs.push("\"\{@core.json_escape(v)}\"")
      }
      // usage 示例:place    [variant|-] [x] [y] [w] [h] k= ...
      let kvs : Array[String] = []
      for param in def.params.iter() {
        kvs.push("\{param.name}=<\{param.kind}>")
      }
      let kv_sb = StringBuilder::new()
      let mut first_kv = true
      for kv in kvs.iter() {
        if first_kv == false {
          kv_sb.write_string(" ")
        }
        first_kv = false
        kv_sb.write_string(kv)
      }
      let kv_j = if kvs.length() > 0 { " " + kv_sb.to_string() } else { "" }
      let usage = "place  \{id}  [variant] [x] [y] [w] [h]\{kv_j}"
      "{\"ok\":true,\"id\":\"\{@core.json_escape(id)}\",\"description\":\"\{@core.json_escape(def.description)}\",\"source\":\"user\",\"size\":\"\{def.width}x\{def.height}\",\"params\":[\{join_arr(ps)}],\"variants\":[\{join_arr(vs)}],\"usage\":\"\{@core.json_escape(usage)}\"}"
    }
  }
}

///|
fn cmd_component_delete(p : @core.Project, parts : Array[String]) -> String {
  if parts.length() < 2 {
    return cli_err("usage: component-delete ")
  }
  let id = parts[1]
  if p.user_components.contains(id) == false {
    return cli_err("unknown_component:" + id)
  }
  p.user_components.remove(id)
  "{\"ok\":true,\"op\":\"component-delete\",\"component\":\"\{@core.json_escape(id)}\"}"
}

///|
/// 库快照:宿主把输出逐项落盘(.mbt.md + 索引)即完成持久化。
fn cmd_library_snapshot(p : @core.Project) -> String {
  let items : Array[String] = []
  for id in p.user_components.keys() {
    match p.user_components.get(id) {
      Some(def) =>
        items.push(
          "{\"id\":\"\{@core.json_escape(id)}\",\"source_b64\":\"\{encode_mbt_b64(def.source)}\"}",
        )
      None => ()
    }
  }
  "{\"ok\":true,\"op\":\"library-snapshot\",\"count\":\{items.length()},\"components\":[\{join_arr(items)}]}"
}

///|
/// 库恢复:宿主会话开始时把本地库逐项灌回(每个参数一个组件声明 b64)。
fn cmd_library_restore(p : @core.Project, parts : Array[String]) -> String {
  if parts.length() < 2 {
    return cli_err("usage: library-restore-b64  [ ...]")
  }
  let mut ok_count = 0
  let mut first_error = ""
  for i = 1; i < parts.length(); i = i + 1 {
    match decode_mbt_b64(parts[i]) {
      Err(error) => if first_error == "" { first_error = error }
      Ok(text) =>
        match @decl.parse_user_component(text) {
          Err(error) => if first_error == "" { first_error = error }
          Ok(def) => {
            p.user_components.set(def.id, def)
            ok_count = ok_count + 1
          }
        }
    }
  }
  if first_error != "" {
    return cli_err("library_restore_partial:\{ok_count}:" + first_error)
  }
  "{\"ok\":true,\"op\":\"library-restore\",\"count\":\{ok_count}}"
}