///|
/// 组件插槽系统:支持嵌套内容分发的组合式组件。
///
/// 定位:**SDK-only 能力**(README「组件插槽系统」,宿主经 MoonBit SDK 直调)——
/// 不进 agent_api 工具字典,CLI/MCP/WASM 三面不暴露;Agent 侧的组合
/// 表达走 place_component + parent_id。
///
/// 这是让组件真正**可组合**的原语——像 React/Vue 的 children/slots:
/// ```jsx
/// 
///   Title
///   Body
/// 
/// ```
/// MoonViz 等价:
/// ```moonbit nocheck
/// p.place_component("card", "my_card")
/// p.fill_slot("my_card", "header", "Title")
/// p.fill_slot("my_card", "content", "Body text here")
/// ```
///
/// - 槽位定义:组件声明命名槽位
/// - 槽位填充:Agent 把内容放进指定槽位
/// - 默认内容:槽位为空时使用默认值
/// - 组合组件:槽位里可以放其他组件(递归组合;SlotDef.accepts 非空时强校验)

// ---------------------------------------------------------------------------
// 槽位定义
// ---------------------------------------------------------------------------

pub(all) struct SlotDef {
  name : String // "header" / "content" / "footer"
  description : String // Agent 可读的槽位说明
  accepts : Array[String] // 接受的组件类型(空 = 任意)
  default_content : String // 默认内容(空串 = 无默认)
}

///|
pub(all) struct SlottedComponent {
  component_id : String // "card" / "list" / "form" / "modal"
  display_name : String
  slots : Array[SlotDef]
  layout : StackLayout // 槽位如何排列
  base_style : NodeStyle
}

// ---------------------------------------------------------------------------
// 内置组合式组件
// ---------------------------------------------------------------------------

///|
pub fn builtin_slotted_components() -> Map[String, SlottedComponent] {
  let m : Map[String, SlottedComponent] = Map::new()

  // ── Card:header + content + footer ──
  m.set("card", {
    component_id: "card",
    display_name: "卡片",
    slots: [
      {
        name: "header",
        description: "卡片头部(标题/头像)",
        accepts: [],
        default_content: "Title",
      },
      {
        name: "content",
        description: "卡片正文内容",
        accepts: [],
        default_content: "",
      },
      {
        name: "footer",
        description: "卡片底部(操作按钮)",
        accepts: [],
        default_content: "",
      },
    ],
    layout: {
      dir: Vertical,
      gap: 8.0,
      justify: Start,
      padding: { top: 16.0, right: 16.0, bottom: 16.0, left: 16.0, },
    },
    base_style: {
      ..NodeStyle::default(),
      fill: "#FFFFFF",
      corner_radius: 12.0,
    },
  })

  // ── List:可变数量 item 槽 ──
  m.set("list", {
    component_id: "list",
    display_name: "列表",
    slots: [
      {
        name: "header",
        description: "列表标题",
        accepts: [],
        default_content: "List",
      },
      {
        name: "item_1",
        description: "列表项 1",
        accepts: [],
        default_content: "Item 1",
      },
      {
        name: "item_2",
        description: "列表项 2",
        accepts: [],
        default_content: "Item 2",
      },
      {
        name: "item_3",
        description: "列表项 3",
        accepts: [],
        default_content: "Item 3",
      },
    ],
    layout: {
      dir: Vertical,
      gap: 4.0,
      justify: Start,
      padding: { top: 8.0, right: 8.0, bottom: 8.0, left: 8.0, },
    },
    base_style: { ..NodeStyle::default(), fill: "#FFFFFF", },
  })

  // ── Form:label + input + error ──
  m.set("form_field", {
    component_id: "form_field",
    display_name: "表单字段",
    slots: [
      {
        name: "label",
        description: "字段标签",
        accepts: [],
        default_content: "Label",
      },
      {
        name: "input",
        description: "输入框",
        accepts: ["text_input"],
        default_content: "",
      },
      {
        name: "error",
        description: "错误提示(可选)",
        accepts: [],
        default_content: "",
      },
    ],
    layout: {
      dir: Vertical,
      gap: 4.0,
      justify: Start,
      padding: { top: 0.0, right: 0.0, bottom: 0.0, left: 0.0, },
    },
    base_style: NodeStyle::default(),
  })

  // ── Modal:title + content + actions ──
  m.set("modal", {
    component_id: "modal",
    display_name: "模态框",
    slots: [
      {
        name: "title",
        description: "弹窗标题",
        accepts: [],
        default_content: "Dialog",
      },
      {
        name: "content",
        description: "弹窗内容",
        accepts: [],
        default_content: "",
      },
      {
        name: "actions",
        description: "操作按钮区域",
        accepts: ["button"],
        default_content: "",
      },
    ],
    layout: {
      dir: Vertical,
      gap: 16.0,
      justify: Start,
      padding: { top: 24.0, right: 24.0, bottom: 24.0, left: 24.0, },
    },
    base_style: {
      ..NodeStyle::default(),
      fill: "#FFFFFF",
      corner_radius: 16.0,
    },
  })

  // ── App Bar:leading + title + trailing ──
  m.set("app_bar", {
    component_id: "app_bar",
    display_name: "应用栏",
    slots: [
      {
        name: "leading",
        description: "左侧(返回箭头/Logo)",
        accepts: [],
        default_content: "←",
      },
      {
        name: "title",
        description: "中间标题",
        accepts: [],
        default_content: "App",
      },
      {
        name: "trailing",
        description: "右侧(更多/菜单)",
        accepts: [],
        default_content: "⋮",
      },
    ],
    layout: {
      dir: Horizontal,
      gap: 12.0,
      justify: Start,
      padding: { top: 16.0, right: 16.0, bottom: 16.0, left: 16.0, },
    },
    base_style: { ..NodeStyle::default(), fill: "#FFFFFF", },
  })

  // ── Tab Bar:可变 tab 槽 ──
  m.set("tab_bar", {
    component_id: "tab_bar",
    display_name: "底部标签栏",
    slots: [
      {
        name: "tab_1",
        description: "标签 1",
        accepts: [],
        default_content: "Home",
      },
      {
        name: "tab_2",
        description: "标签 2",
        accepts: [],
        default_content: "Search",
      },
      {
        name: "tab_3",
        description: "标签 3",
        accepts: [],
        default_content: "Profile",
      },
      {
        name: "tab_4",
        description: "标签 4(可选)",
        accepts: [],
        default_content: "",
      },
    ],
    layout: {
      dir: Horizontal,
      gap: 0.0,
      justify: Start,
      padding: { top: 8.0, right: 0.0, bottom: 8.0, left: 0.0, },
    },
    base_style: { ..NodeStyle::default(), fill: "#FFFFFF", },
  })

  m
}

///|
/// 组合式组件清单 JSON(Agent 发现用)
pub fn list_slotted_json() -> String {
  let m = builtin_slotted_components()
  let parts : Array[String] = []
  for id in m.keys() {
    match m.get(id) {
      Some(c) => {
        let slots : Array[String] = []
        for s in c.slots.iter() {
          slots.push("\"" + s.name + "\"")
        }
        let sj = jstr22(slots)
        parts.push(
          "{\"id\":\"\{c.component_id}\",\"name\":\"\{c.display_name}\",\"slots\":[\{sj}]}",
        )
      }
      None => ()
    }
  }
  let j = jstr22(parts)
  "[\{j}]"
}

// ---------------------------------------------------------------------------
// Project 集成:放置组合式组件并填充槽位
// ---------------------------------------------------------------------------

///|
/// 放置组合式组件(自动创建槽位子节点)
pub fn Project::place_slotted(
  self : Project,
  artboard~ : String,
  component_id~ : String,
  instance_id~ : String,
  x? : Double = 0.0,
  y? : Double = 0.0,
) -> Result[String, String] {
  let slot_comps = builtin_slotted_components()
  match slot_comps.get(component_id) {
    None => Err("unknown_slotted_component: " + component_id)
    Some(sc) =>
      match self.artboards.get(artboard) {
        None => Err("unknown_artboard")
        Some(ab) => {
          // 创建父容器
          let layout = solve(ab.doc)
          let root_w = match layout.rect_of("root") {
            Some(r) => r.width
            None => 390.0
          }
          let parent = {
            ..Node::make(instance_id, Frame),
            name: sc.display_name,
            width: Fixed(root_w - 48.0),
            height: Hug,
            x: AlignStart(x),
            y: AlignStart(y),
            layout: Some(sc.layout),
            style: sc.base_style,
            component: sc.component_id,
          }
          match ab.doc.insert_node(parent, "root") {
            true => {
              // 为每个槽位创建子节点(含默认内容)
              let slot_ids : Array[String] = []
              for slot in sc.slots.iter() {
                let slot_node_id = instance_id + "_" + slot.name
                let content = if slot.default_content != "" {
                  slot.default_content
                } else {
                  ""
                }
                let slot_node = {
                  ..Node::make(slot_node_id, Text),
                  name: slot.name,
                  text: content,
                  width: Fill,
                  height: Hug,
                  x: AlignStart(0.0),
                  y: AlignStart(0.0),
                  style: {
                    ..NodeStyle::default(),
                    font_size: if slot.name == "title" || slot.name == "header" {
                      18.0
                    } else {
                      14.0
                    },
                  },
                  component: "slot_" + slot.name,
                }
                let _ = ab.doc.insert_node(slot_node, instance_id)
                slot_ids.push(slot_node_id)
              }
              let sj = jstr22(slot_ids)
              Ok(
                "{\"ok\":true,\"instance\":\"\{instance_id}\",\"slots\":\{sc.slots.length()},\"slot_nodes\":[\{sj}]}",
              )
            }
            false => Err("insert_failed")
          }
        }
      }
  }
}

///|
/// 填充槽位内容
pub fn Project::fill_slot(
  self : Project,
  artboard~ : String,
  instance_id~ : String,
  slot_name~ : String,
  content~ : String,
) -> Result[String, String] {
  let slot_node_id = instance_id + "_" + slot_name
  match self.artboards.get(artboard) {
    None => Err("unknown_artboard")
    Some(ab) => {
      if !ab.doc.has(slot_node_id) {
        return Err("slot_not_found: " + slot_node_id)
      }
      let _ = ab.doc.update_node(slot_node_id, fn(n) { { ..n, text: content, } })
      // envelope 合法性红线:content 含引号/反斜杠时裸拼会产出非法 JSON
      Ok(
        "{\"ok\":true,\"slot\":\"\{json_escape(slot_name)}\",\"content\":\"\{json_escape(content)}\"}",
      )
    }
  }
}

///|
/// 获取槽位当前内容
pub fn Project::get_slot(
  self : Project,
  artboard~ : String,
  instance_id~ : String,
  slot_name~ : String,
) -> Result[String, String] {
  let slot_node_id = instance_id + "_" + slot_name
  match self.artboards.get(artboard) {
    None => Err("unknown_artboard")
    Some(ab) =>
      match ab.doc.get(slot_node_id) {
        Some(n) => Ok(n.text)
        None => Err("slot_not_found")
      }
  }
}

///|
/// 列出实例的所有槽位及其内容
pub fn Project::list_slots(
  self : Project,
  artboard~ : String,
  instance_id~ : String,
) -> Result[String, String] {
  let slot_comps = builtin_slotted_components()
  match self.artboards.get(artboard) {
    None => Err("unknown_artboard")
    Some(ab) => {
      // 找到组件类型
      let comp_type = match ab.doc.get(instance_id) {
        Some(n) => n.component
        None => return Err("instance_not_found")
      }
      match slot_comps.get(comp_type) {
        None => Err("not_a_slotted_component")
        Some(sc) => {
          let slots : Array[String] = []
          for slot in sc.slots.iter() {
            let slot_id = instance_id + "_" + slot.name
            let content = match ab.doc.get(slot_id) {
              Some(n) => n.text
              None => ""
            }
            slots.push(
              "{\"name\":\"\{json_escape(slot.name)}\",\"content\":\"\{json_escape(content)}\",\"description\":\"\{json_escape(slot.description)}\"}",
            )
          }
          let j = jstr22(slots)
          Ok("[\{j}]")
        }
      }
    }
  }
}

///|
/// 向槽位中放入另一个组件(递归组合)
pub fn Project::fill_slot_with_component(
  self : Project,
  artboard~ : String,
  instance_id~ : String,
  slot_name~ : String,
  child_component~ : String,
  child_id~ : String,
) -> Result[String, String] {
  let slot_node_id = instance_id + "_" + slot_name
  match self.artboards.get(artboard) {
    None => Err("unknown_artboard")
    Some(ab) => {
      if !ab.doc.has(slot_node_id) {
        return Err("slot_not_found")
      }
      // SlotDef.accepts 校验(空数组 = 任意组件):约束不是摆设,
      // 违约放置在这里显式拒绝而非静默落位
      let comp_type = match ab.doc.get(instance_id) {
        Some(n) => n.component
        None => return Err("instance_not_found")
      }
      match builtin_slotted_components().get(comp_type) {
        Some(sc) => {
          let mut accepts : Array[String] = []
          for s in sc.slots.iter() {
            if s.name == slot_name {
              accepts = s.accepts
            }
          }
          if accepts.length() > 0 {
            let mut allowed = false
            for a in accepts.iter() {
              if a == child_component {
                allowed = true
              }
            }
            if !allowed {
              return Err(
                "slot_accepts_violation: slot \"\{slot_name}\" accepts [\{jstr22(accepts)}], got \"\{child_component}\"",
              )
            }
          }
        }
        None => () // 非组合式组件实例:沿用旧行为,仅要求槽位节点存在
      }
      // 在槽位节点内放置子组件
      let comps = builtin_components()
      match comps.get(child_component) {
        Some(def) => {
          let node = instantiate_component(def, child_id, tokens=self.tokens)
          let placed = {
            ..node,
            width: Fill,
            x: AlignStart(0.0),
            y: AlignStart(0.0),
          }
          match ab.doc.insert_node(placed, slot_node_id) {
            true =>
              Ok(
                "{\"ok\":true,\"slot\":\"\{slot_name}\",\"component\":\"\{child_component}\",\"id\":\"\{child_id}\"}",
              )
            false => Err("insert_failed")
          }
        }
        None => Err("unknown_component: " + child_component)
      }
    }
  }
}

///|
fn jstr22(parts : Array[String]) -> String {
  let sb = StringBuilder::new()
  for i in 0.. 0 {
      sb.write_string(",")
    }
    sb.write_string(parts[i])
  }
  sb.to_string()
}