/// decompose.mbt —— AO orchestrator 式递归任务拆解器(M3:AO 式拆解)
///
/// 语义参考 agent-orchestrator(backend/internal/skillassets/using-ao/commands):
///   1. orchestrator:持久规划 agent,把 plan 拆成 focused tasks
///      —— 映射为 FistEngine::plan_deep 递归拆解器;
///   2. project add:注册仓库 + 配置 worker/orchestrator rules
///      —— 联动:根任务 project_dir 落库,拆出的子任务继承 project_dir,
///         供执行者 claim / execute(即 AO spawn 派发 worker);
///   3. spawn:派发 worker —— 拆出的子任务挂 parent_id/depth,由执行者 claim;
///   4. review:按 omega spec 验收 —— 每条拆解依据带 spec_hash
///         (取 spec.fingerprint 作验收基准,供后续 omega_verify 对照)。
///
/// 结构定位(拳长那套分层递归):根任务 depth=K,每拆一层减 1,
/// is_leaf = depth<=1 即停止;任一层可被下一层拳长继续递归拆分。
/// 全过程写 StoreBackend(SQLite 内建数据库),重启后经 get_task 即可恢复完整子树
/// (M3 验收标准)。

/// 从 omega spec JSON 提取切割依据(slices)。
/// 规则:
///   - spec 为对象且 laws 为非空字符串数组 → 以 laws 为准(AO "break into focused
///     tasks":每条 law 一个聚焦子任务,等价于把 plan 拆成可执行单元);
///   - 否则 → 退化为按 split_n 生成通用拆解条目。
/// 返回切片描述数组。
pub fn decompose_slices(spec : Json, split_n : Int, parent_desc~ : String) -> Array[String] {
  match spec {
    Object(_) =>
      match spec.value("laws") {
        Some(Array(laws)) => {
          let out : Array[String] = []
          for l in laws {
            match l {
              String(s) if s.trim() != "" => out.push(s)
              _ => ()
            }
          }
          if out.is_empty() {
            default_slices(split_n, parent_desc=parent_desc)
          } else {
            out
          }
        }
        _ => default_slices(split_n, parent_desc=parent_desc)
      }
    _ => default_slices(split_n, parent_desc=parent_desc)
  }
}

/// 无 spec.laws 时的通用切分条目(描述逐层递进派生)。
fn default_slices(split_n : Int, parent_desc~ : String) -> Array[String] {
  let out : Array[String] = []
  for i = 1; i <= split_n; i = i + 1 {
    out.push("\{parent_desc} - 子任务单元 \{i}")
  }
  out
}

/// 取 spec 的 fingerprint 作为验收基准 spec_hash;无 spec / 无 fingerprint 为空串。
fn spec_hash_of(spec : Json?) -> String {
  match spec {
    Some(s) =>
      match s.value("fingerprint") {
        Some(String(f)) => f
        _ => ""
      }
    None => ""
  }
}

/// 内部迁移:将父任务标记为拆分中。
/// AO 拳长可在任务未认领(待领取)时直接拆解,故宽于 Task::split(仅限已领取);
/// 已完成/已归档等终态仍拒绝(由 plan_deep 前置校验拦截并给出可读错误)。
fn Task::mark_decomposing(self : Task) -> Result[Task, String] {
  match self.status {
    Pending | Claimed | Splitting => Ok({ ..self, status: Splitting })
    _ => Err("非法拆解: 任务处于 [\{self.status.to_string()}]")
  }
}

/// AO 式递归拆解入口:对指定任务拆出整棵子任务树并写库。
///
/// 入参:
///   task_id   要拆解的根/拳节任务 id(非原子任务才可拆);
///   split_n   每层默认拆分数(无 spec.laws 时生效,默认 3);
///   by        执行拆解的拳长身份(仅记录);
///   spec      可选 omega spec JSON(laws 作切割依据,fingerprint 作验收基准);
///   now       时间戳。
///
/// 返回:{ "root", "by", "created", "tree" },tree 为多层子树结构;
/// 任一子节点非叶时将继续递归(depth 递减,is_leaf=depth<=1 停止)。
pub fn FistEngine::plan_deep(
  self : FistEngine,
  task_id~ : String,
  split_n~ : Int = 3,
  by~ : String,
  spec~ : Json? = None,
  now~ : String,
) -> Result[Json, String] {
  match self.store.get_task(task_id) {
    None => Err("task not found: \{task_id}")
    Some(parent) => {
      if parent.is_leaf() {
        return Err("原子任务不可再拆分: \{task_id}")
      }
      let st = parent.get_status()
      if st != Claimed && st != Pending && st != Splitting {
        return Err("非法拆解: 任务处于 [\{parent.status_to_string()}]")
      }
      if split_n < 1 {
        return Err("split_n 必须 >= 1")
      }
      let slices = match spec {
        Some(s) => decompose_slices(s, split_n, parent_desc=parent.description)
        None => default_slices(split_n, parent_desc=parent.description)
      }
      match self.decompose_rec(
        parent,
        slices,
        spec,
        now,
      ) {
        Err(e) => Err(e)
        Ok(tree) =>
          Ok(Json::object({
            "root": Json::string(task_id),
            "by": Json::string(by),
            "split_n": Json::number(split_n.to_double()),
            "tree": tree,
          }))
      }
    }
  }
}

/// 递归拆解单层并下钻(私有,同包可见)。
/// 返回该层子树 meta:{ "task_id", "created", "children": [...] }。
fn FistEngine::decompose_rec(
  self : FistEngine,
  parent : Task,
  slices : Array[String],
  spec : Json?,
  now : String,
) -> Result[Json, String] {
  // 父任务进入拆分中(宽于 Task::split:待领取/已领取/拆分中均可转入)
  match parent.mark_decomposing() {
    Ok(p2) => ignore(self.store.update_task({ ..p2, updated_at: now, }))
    Err(e) => return Err(e)
  }
  let child_depth = parent.get_depth() - 1
  let spec_hash = spec_hash_of(spec)
  let nodes : Array[Json] = []
  let mut created = 0
  for i = 0; i < slices.length(); i = i + 1 {
    let idx = i + 1
    let cid = child_id(parent_id=parent.get_id(), idx=idx)
    let slice_text = slices[i]
    let full_desc = if spec_hash == "" {
      slice_text
    } else {
      "\{slice_text} [spec:\{spec_hash}]"
    }
    let child = Task::new(
      id=cid,
      project_dir=parent.project_dir,
      description=full_desc,
      parent_id=parent.get_id(),
      priority=parent.priority,
      importance=parent.importance,
      depth=child_depth,
      split_n=if child_depth > 1 { child_depth } else { 1 },
      created_at=now,
    )
    match self.store.create_task(child) {
      Ok(_) => () // 已入 created 计数
      Err(e) => return Err(e)
    }
    created = created + 1
    // 递归下钻:仅非叶节点继续派生拆分(下一层用通用切分细化描述)
    let kids_arr : Array[Json] = if not(child.is_leaf()) {
      let next_slices = default_slices(child.split_n, parent_desc=child.description)
      match self.decompose_rec(child, next_slices, spec, now) {
        Ok(subtree) =>
          match subtree.value("children") {
            Some(Array(a)) => a
            _ => []
          }
        Err(e) => return Err(e)
      }
    } else {
      []
    }
    let node_m : Map[String, Json] = Map([
      ("id", Json::string(cid)),
      ("depth", Json::number(child_depth.to_double())),
      ("leaf", Json::boolean(child.is_leaf())),
      ("spec_hash", Json::string(spec_hash)),
      ("children", Json::array(kids_arr)),
    ])
    nodes.push(Json::object(node_m))
  }
  Ok(Json::object({
    "task_id": Json::string(parent.get_id()),
    "created": Json::number(created.to_double()),
    "children": Json::array(nodes),
  }))
}