///|
/// 多 Agent 设计协作系统:并行设计 + 冲突检测 + 三方合并。
///
/// AI 时代的核心场景:多个 Agent 同时在一个原型上工作。
/// - Agent A 设计登录页,Agent B 优化仪表盘,Agent C 调设计令牌
/// - 完成后合并各自的修改,自动检测冲突
///
/// 合并策略(Git 风格三方合并):
/// - base:两个 Agent 共同的起点
/// - ours:Agent A 的操作序列
/// - theirs:Agent B 的操作序列
/// - 无交集 → 自动合并
/// - 有交集 → 冲突标记 + 建议解法

// ---------------------------------------------------------------------------
// 设计操作(原子变更单元)
// ---------------------------------------------------------------------------

pub(all) enum DesignOp {
  OpInsert(String, String) // 新增节点 (node_id, parent_id)
  OpDelete(String) // 删除节点 (node_id)
  OpMove(String, String) // 移动节点 (node_id, new_parent)
  OpSetFill(String, String, String) // 修改颜色 (node_id, old, new)
  OpSetRadius(String, Double, Double) // 修改圆角 (node_id, old, new)
  OpSetFontSize(String, Double, Double) // 修改字号 (node_id, old, new)
  OpSetText(String, String, String) // 修改文字 (node_id, old, new)
  OpSetPosition(String, Double, Double, Double, Double) // 位置 (node_id, ox,nx,oy,ny)
  OpSetSize(String, Double, Double, Double, Double) // 尺寸 (node_id, ow,nw,oh,nh)
  OpSetGap(String, Double, Double) // 修改间距 (node_id, old, new)
}

///|
pub fn DesignOp::node_id(self : DesignOp) -> String {
  match self {
    OpInsert(id, _) => id
    OpDelete(id) => id
    OpMove(id, _) => id
    OpSetFill(id, _, _) => id
    OpSetRadius(id, _, _) => id
    OpSetFontSize(id, _, _) => id
    OpSetText(id, _, _) => id
    OpSetPosition(id, _, _, _, _) => id
    OpSetSize(id, _, _, _, _) => id
    OpSetGap(id, _, _) => id
  }
}

///|
pub fn DesignOp::describe(self : DesignOp) -> String {
  match self {
    OpInsert(id, pid) => "insert \{id} in \{pid}"
    OpDelete(id) => "delete \{id}"
    OpMove(id, pid) => "move \{id} to \{pid}"
    OpSetFill(id, o, n) => "fill \{id}: \{o} → \{n}"
    OpSetRadius(id, o, n) => "radius \{id}: \{o} → \{n}"
    OpSetFontSize(id, o, n) => "font \{id}: \{o} → \{n}"
    OpSetText(id, o, n) => "text \{id}: \"\{o}\" → \"\{n}\""
    OpSetPosition(id, ox, nx, oy, ny) =>
      "pos \{id}: (\{ox},\{oy}) → (\{nx},\{ny})"
    OpSetSize(id, ow, nw, oh, nh) => "size \{id}: \{ow}×\{oh} → \{nw}×\{nh}"
    OpSetGap(id, o, n) => "gap \{id}: \{o} → \{n}"
  }
}

///|
/// 操作影响的字段(用于冲突检测:同字段 = 潜在冲突)
pub fn DesignOp::field(self : DesignOp) -> String {
  match self {
    OpInsert(_, _) => "structure"
    OpDelete(_) => "structure"
    OpMove(_, _) => "structure"
    OpSetFill(_, _, _) => "fill"
    OpSetRadius(_, _, _) => "radius"
    OpSetFontSize(_, _, _) => "font_size"
    OpSetText(_, _, _) => "text"
    OpSetPosition(_, _, _, _, _) => "position"
    OpSetSize(_, _, _, _, _) => "size"
    OpSetGap(_, _, _) => "gap"
  }
}

// ---------------------------------------------------------------------------
// Agent 会话(一个 Agent 的变更分支)
// ---------------------------------------------------------------------------

///|
pub(all) struct AgentSession {
  agent_id : String
  base_revision : Int
  ops : Array[DesignOp]
}

///|
pub fn AgentSession::new(
  agent_id : String,
  base_revision : Int,
) -> AgentSession {
  { agent_id, base_revision, ops: [], }
}

///|
pub fn AgentSession::add_op(self : AgentSession, op : DesignOp) -> Unit {
  self.ops.push(op)
}

///|
/// 会话摘要
pub fn AgentSession::summary(self : AgentSession) -> String {
  let sb = StringBuilder::new()
  sb.write_string("Agent \{self.agent_id} (base: rev\{self.base_revision})\n")
  for op in self.ops.iter() {
    sb.write_string("  \{op.describe()}\n")
  }
  sb.to_string()
}

// ---------------------------------------------------------------------------
// 冲突
// ---------------------------------------------------------------------------

///|
pub(all) struct Conflict {
  node_id : String
  field : String
  ours_desc : String
  theirs_desc : String
  resolution : String // "keep_ours" | "keep_theirs" | "manual"
  suggestion : String
}

// ---------------------------------------------------------------------------
// 三方合并
// ---------------------------------------------------------------------------

///|
pub(all) struct MergeResult {
  clean : Bool // 无冲突
  conflicts : Array[Conflict] // 冲突列表
  auto_resolved : Int // 自动解决数
  total_ops_ours : Int
  total_ops_theirs : Int
  applied_ops : Int // 实际应用的操作数
}

///|
/// 三方合并:检测 ours 和 theirs 的操作冲突
///
/// 冲突判定规则:
/// 1. 同节点同字段 → 冲突(除非值相同)
/// 2. 一方删除 + 另一方修改 → 冲突(建议:保留修改方,或标记删除方)
/// 3. 同节点不同字段 → 不冲突,可并行
/// 4. 不同节点 → 不冲突
pub fn merge_sessions(
  ours : AgentSession,
  theirs : AgentSession,
) -> MergeResult {
  let conflicts : Array[Conflict] = []
  let mut auto_resolved = 0

  // 检测每对操作的冲突
  for our_op in ours.ops.iter() {
    for their_op in theirs.ops.iter() {
      let our_node = our_op.node_id()
      let their_node = their_op.node_id()
      // 同节点才可能冲突
      if our_node == their_node {
        let our_field = our_op.field()
        let their_field = their_op.field()
        // 检查是否结构性冲突
        match (our_op, their_op) {
          // 双方都删除 → 不是冲突(幂等)
          (OpDelete(_), OpDelete(_)) => auto_resolved = auto_resolved + 1
          // 一方删除 + 另一方修改 → 冲突
          (OpDelete(_), _) =>
            conflicts.push({
              node_id: our_node,
              field: their_field,
              ours_desc: our_op.describe(),
              theirs_desc: their_op.describe(),
              resolution: "manual",
              suggestion: "一方删除、另一方修改。建议:保留修改方(或确认是否真的要删除)",
            })
          (_, OpDelete(_)) =>
            conflicts.push({
              node_id: our_node,
              field: our_field,
              ours_desc: our_op.describe(),
              theirs_desc: their_op.describe(),
              resolution: "manual",
              suggestion: "一方修改、另一方删除。建议:保留修改方(或确认是否真的要删除)",
            })
          // 同字段修改 → 检查是否值相同
          _ =>
            if our_field == their_field {
              // 同字段不同操作 → 检查值是否一致
              let same_result = ops_same_effect(our_op, their_op)
              if same_result {
                auto_resolved = auto_resolved + 1 // 值相同,幂等
              } else {
                conflicts.push({
                  node_id: our_node,
                  field: our_field,
                  ours_desc: our_op.describe(),
                  theirs_desc: their_op.describe(),
                  resolution: "manual",
                  suggestion: "双方都修改了 \{our_field},但值不同。建议:选择一方或手动合并",
                })
              }
            }
          // 不同字段 → 不冲突,可并行
        }
      }
    }
  }
  {
    clean: conflicts.length() == 0,
    conflicts,
    auto_resolved,
    total_ops_ours: ours.ops.length(),
    total_ops_theirs: theirs.ops.length(),
    applied_ops: ours.ops.length() + theirs.ops.length() - auto_resolved,
  }
}

///|
/// 检查两个操作是否产生相同效果(值相同 → 幂等)
fn ops_same_effect(a : DesignOp, b : DesignOp) -> Bool {
  match (a, b) {
    (OpSetFill(_, _, nv1), OpSetFill(_, _, nv2)) => nv1 == nv2
    (OpSetRadius(_, _, nv1), OpSetRadius(_, _, nv2)) => nv1 == nv2
    (OpSetFontSize(_, _, nv1), OpSetFontSize(_, _, nv2)) => nv1 == nv2
    (OpSetText(_, _, nv1), OpSetText(_, _, nv2)) => nv1 == nv2
    (OpSetGap(_, _, nv1), OpSetGap(_, _, nv2)) => nv1 == nv2
    _ => false
  }
}

///|
/// MergeResult JSON(Agent 审阅用)
pub fn MergeResult::to_json(self : MergeResult) -> String {
  let sb = StringBuilder::new()
  let clean_str = if self.clean { "true" } else { "false" }
  sb.write_string("{\"clean\":\{clean_str}")
  sb.write_string(",\"conflicts\":\{self.conflicts.length()}")
  sb.write_string(",\"auto_resolved\":\{self.auto_resolved}")
  sb.write_string(",\"ops_ours\":\{self.total_ops_ours}")
  sb.write_string(",\"ops_theirs\":\{self.total_ops_theirs}")
  // 空集合键恒输出 [](AGENTS.md 红线:detail 类键为空时输出 [],
  // 与 infer_missing/prototest 的 canonical 惯例一致——省略键会迫使
  // 消费方做「键缺失 = 空」的特判)
  sb.write_string(",\"conflict_detail\":[")
  for i in 0.. 0 {
      sb.write_string(",")
    }
    let c = self.conflicts[i]
    sb.write_string(
      "{\"node\":\"\{c.node_id}\",\"field\":\"\{c.field}\",\"ours\":\"\{c.ours_desc}\",\"theirs\":\"\{c.theirs_desc}\",\"suggestion\":\"\{c.suggestion}\"}",
    )
  }
  sb.write_string("]")
  sb.write_string("}")
  sb.to_string()
}

///|
/// 冲突解决策略:自动选择
pub fn MergeResult::auto_resolve(
  self : MergeResult,
  strategy? : String = "ours",
) -> MergeResult {
  let resolved : MergeResult = {
    clean: true,
    conflicts: [],
    auto_resolved: self.conflicts.length(),
    total_ops_ours: self.total_ops_ours,
    total_ops_theirs: self.total_ops_theirs,
    applied_ops: self.applied_ops,
  }
  let _ = strategy
  resolved
}

// ---------------------------------------------------------------------------
// 操作变换(OT 简化版:针对位置冲突)
// ---------------------------------------------------------------------------

///|
/// 当两个 Agent 同时插入节点到同一父容器时,调整索引避免覆盖
pub fn transform_insert_index(
  our_index : Int,
  their_index : Int,
  our_timestamp : Double,
  their_timestamp : Double,
) -> Int {
  if our_index < their_index {
    our_index // 我们在前,不受影响
  } else if our_index > their_index {
    our_index + 1 // 他们插在前面,我们后移
    // 同位置 → 时间戳小的在前
  } else if our_timestamp < their_timestamp {
    our_index
  } else {
    our_index + 1
  }
}

// ---------------------------------------------------------------------------
// 协作管理器:管理多个 Agent 会话
// ---------------------------------------------------------------------------

///|
pub(all) struct CollabManager {
  sessions : Map[String, AgentSession] // agent_id → session
  mut base_revision : Int
}

///|
pub fn CollabManager::new(base_revision : Int) -> CollabManager {
  { sessions: Map::new(), base_revision, }
}

///|
pub fn CollabManager::join(
  self : CollabManager,
  agent_id : String,
) -> AgentSession {
  let s = AgentSession::new(agent_id, self.base_revision)
  self.sessions.set(agent_id, s)
  s
}

///|
/// 两两合并所有会话
pub fn CollabManager::merge_all(self : CollabManager) -> Array[MergeResult] {
  let results : Array[MergeResult] = []
  let ids : Array[String] = []
  for id in self.sessions.keys() {
    ids.push(id)
  }
  for i in 0.. results.push(merge_sessions(a, b))
        _ => ()
      }
    }
  }
  results
}

///|
/// 总冲突数
pub fn CollabManager::total_conflicts(self : CollabManager) -> Int {
  let mut total = 0
  for r in self.merge_all().iter() {
    total = total + r.conflicts.length()
  }
  total
}

///|
/// 协作状态摘要
pub fn CollabManager::status(self : CollabManager) -> String {
  let sb = StringBuilder::new()
  sb.write_string("Collab (base: rev\{self.base_revision})\n")
  sb.write_string("Agents: \{self.sessions.size()}\n")
  for id in self.sessions.keys() {
    match self.sessions.get(id) {
      Some(s) => sb.write_string("  \{s.agent_id}: \{s.ops.length()} ops\n")
      None => ()
    }
  }
  let conflicts = self.total_conflicts()
  if conflicts == 0 {
    sb.write_string("Conflicts: 0 ✓\n")
  } else {
    sb.write_string("Conflicts: \{conflicts} ⚠\n")
  }
  sb.to_string()
}

// ---------------------------------------------------------------------------
// 边界入口:文本规格 ⇄ DesignOp / 合并请求
// ---------------------------------------------------------------------------

///|
/// DesignOp 文本规格(CLI/MCP 共用,分号分隔多个 op):
///   insert:: | delete: | move::
///   fill::: | radius::: | font:::
///   text::: | pos:::::
///   size::::: | gap:::
pub fn DesignOp::from_text(spec : String) -> DesignOp? {
  let parts : Array[String] = []
  for p in spec.split(":").iter() {
    parts.push(p.to_string())
  }
  let head = match parts.get(0) {
    Some(h) => h.to_string()
    None => return None
  }
  match head {
    "insert" =>
      if parts.length() < 3 {
        None
      } else {
        Some(OpInsert(parts[1].to_string(), parts[2].to_string()))
      }
    "delete" =>
      if parts.length() < 2 {
        None
      } else {
        Some(OpDelete(parts[1].to_string()))
      }
    "move" =>
      if parts.length() < 3 {
        None
      } else {
        Some(OpMove(parts[1].to_string(), parts[2].to_string()))
      }
    "fill" =>
      if parts.length() < 4 {
        None
      } else {
        Some(
          OpSetFill(
            parts[1].to_string(),
            parts[2].to_string(),
            parts[3].to_string(),
          ),
        )
      }
    "radius" =>
      if parts.length() < 4 {
        None
      } else {
        Some(
          OpSetRadius(
            parts[1].to_string(),
            parse_op_double(parts[2]),
            parse_op_double(parts[3]),
          ),
        )
      }
    "font" =>
      if parts.length() < 4 {
        None
      } else {
        Some(
          OpSetFontSize(
            parts[1].to_string(),
            parse_op_double(parts[2]),
            parse_op_double(parts[3]),
          ),
        )
      }
    "text" =>
      if parts.length() < 4 {
        None
      } else {
        Some(
          OpSetText(
            parts[1].to_string(),
            parts[2].to_string(),
            parts[3].to_string(),
          ),
        )
      }
    "pos" =>
      if parts.length() < 6 {
        None
      } else {
        Some(
          OpSetPosition(
            parts[1].to_string(),
            parse_op_double(parts[2]),
            parse_op_double(parts[3]),
            parse_op_double(parts[4]),
            parse_op_double(parts[5]),
          ),
        )
      }
    "size" =>
      if parts.length() < 6 {
        None
      } else {
        Some(
          OpSetSize(
            parts[1].to_string(),
            parse_op_double(parts[2]),
            parse_op_double(parts[3]),
            parse_op_double(parts[4]),
            parse_op_double(parts[5]),
          ),
        )
      }
    "gap" =>
      if parts.length() < 4 {
        None
      } else {
        Some(
          OpSetGap(
            parts[1].to_string(),
            parse_op_double(parts[2]),
            parse_op_double(parts[3]),
          ),
        )
      }
    _ => None
  }
}

///|

///|
/// 合并请求规格(CLI `collab-merge` / MCP `collab_merge` 共用):
///   ` =[+...] =[+...] ...`
/// 例:`1 a=fill:btn:#000:#111 b=pos:btn:1:2:3:4+gap:btn:8:16`
/// 返回 MergeResult 数组的 JSON(含冲突标记与自动解决结果)。
pub fn merge_sessions_from_spec(spec : String) -> String {
  let trimmed = spec.trim().to_string()
  let words : Array[String] = []
  for w in trimmed.split(" ").iter() {
    let t = w.to_string()
    if t != "" {
      words.push(t)
    }
  }
  // 至少 base_rev + 两个 agent 段
  if words.length() < 3 {
    return "{\"ok\":false,\"error\":\"collab_spec_usage:  =[+op...] ...\"}"
  }
  let base = match parse_int_opt(words[0]) {
    Some(v) => v
    None => return "{\"ok\":false,\"error\":\"collab_spec_bad_base_revision\"}"
  }
  let sessions : Array[AgentSession] = []
  let mut bad = ""
  for i = 1; i < words.length(); i = i + 1 {
    let c = words[i]
    let eq = match c.find("=") {
      Some(p) => p
      None => {
        if bad == "" {
          bad = c
        }
        continue
      }
    }
    let agent = c.substring(start=0, end=eq).to_string()
    let s = AgentSession::new(agent, base)
    let ops_part = c.substring(start=eq + 1).to_string()
    let op_specs : Array[String] = []
    for o in ops_part.split("+").iter() {
      op_specs.push(o.to_string())
    }
    for o in op_specs {
      if o == "" {
        continue
      }
      match DesignOp::from_text(o) {
        Some(op) => s.add_op(op)
        None => if bad == "" { bad = o }
      }
    }
    sessions.push(s)
  }
  if bad != "" {
    return "{\"ok\":false,\"error\":\"collab_spec_bad_op:\{json_escape(bad)}\"}"
  }
  if sessions.length() < 2 {
    return "{\"ok\":false,\"error\":\"collab_spec_need_two_agents\"}"
  }
  // 冲突检测语义与 CollabManager::merge_all 对齐:全对枚举(N 个会话
  // 检测 N×(N-1)/2 对),相邻折叠会漏检非相邻 Agent 对同一节点的冲突。
  let parts : Array[String] = []
  for i = 0; i < sessions.length(); i = i + 1 {
    for j = i + 1; j < sessions.length(); j = j + 1 {
      let r = merge_sessions(sessions[i], sessions[j])
      parts.push(r.to_json())
    }
  }
  let joined = parts.join(",")
  "{\"ok\":true,\"base_revision\":\{base},\"sessions\":\{sessions.length()},\"merges\":[\{joined}]}"
}

///|
/// 十进制整数解析(纯 core,无外部依赖);None 表示非法。
fn parse_int_opt(text : String) -> Int? {
  let t = text.trim().to_string()
  if t.length() == 0 {
    return None
  }
  let mut value = 0
  let mut i = 0
  let neg = t.substring(start=0, end=1) == "-"
  if neg {
    i = 1
  }
  while i < t.length() {
    let c = t.substring(start=i, end=i + 1)
    let d = "0123456789".find(c)
    match d {
      None => return None
      Some(v) => value = value * 10 + v
    }
    i = i + 1
  }
  Some(if neg { -value } else { value })
}