///|
/// 设计标注系统:自动从原型生成开发者交接规范(Spec)。
///
/// 这是 Figma Dev Mode / Zeplin 的等价物:
/// - 间距标注:每个元素到父容器/兄弟的精确距离
/// - 颜色标注:所有用到的颜色及其语义令牌名
/// - 字体标注:字号/颜色/令牌映射
/// - 组件清单:用了哪些组件、每个的变体和属性
/// - 交互标注:点击/滑动触发什么
///
/// 输出为结构化 Markdown,工程师直接复制使用。

// ---------------------------------------------------------------------------
// 标注数据
// ---------------------------------------------------------------------------

pub(all) struct SpacingNote {
  node_id : String
  label : String // "margin-left" / "gap" / "padding"
  value : Double
  unit : String // "px"
}

///|
pub(all) struct ColorNote {
  node_id : String
  field : String // "fill" / "text_color" / "stroke"
  hex : String
  token_name : String // 匹配到的令牌名(或 "custom")
}

///|
pub(all) struct FontNote {
  node_id : String
  size : Double
  color : String
  token_hint : String // "h2" / "body" / "caption" / "custom"
}

///|
pub(all) struct ComponentNote {
  node_id : String
  component : String
  variant : String
  props : String // 序列化属性
}

// ---------------------------------------------------------------------------
// 标注生成
// ---------------------------------------------------------------------------

///|
/// 生成画板的完整设计规范(Markdown 格式)
pub fn Project::generate_spec(
  self : Project,
  artboard~ : String,
) -> Result[String, String] {
  match self.artboards.get(artboard) {
    None => Err("unknown_artboard")
    Some(ab) => {
      let layout = solve(ab.doc)
      let tokens = self.tokens
      let sb = StringBuilder::new()

      // 头部
      sb.write_string("# Design Spec: \{ab.name}\n\n")
      sb.write_string("> Auto-generated by MoonViz Agent Engine\n\n")
      sb.write_string(
        "**Artboard**: \{ab.width.to_int()} × \{ab.height.to_int()}px\n\n",
      )
      sb.write_string("---\n\n")

      // ── 组件清单 ──
      sb.write_string("## Components\n\n")
      sb.write_string("| Component | Instance | Variant | Dimensions |\n")
      sb.write_string("|:---|:---|:---|:---|\n")
      for id in ab.doc.nodes.keys() {
        match ab.doc.nodes.get(id) {
          Some(n) =>
            if id != "root" && n.component != "" {
              match layout.rect_of(id) {
                Some(r) => {
                  let w = r.width.to_int().to_string()
                  let h = r.height.to_int().to_string()
                  let x = r.x.to_int().to_string()
                  let y = r.y.to_int().to_string()
                  sb.write_string(
                    "| `\{n.component}` | `\{id}` | default | \{w}×\{h} @ (\{x},\{y}) |\n",
                  )
                }
                None => ()
              }
            }
          None => ()
        }
      }
      sb.write_string("\n")

      // ── 颜色标注 ──
      sb.write_string("## Colors\n\n")
      sb.write_string("| Node | Field | Value | Token |\n")
      sb.write_string("|:---|:---|:---|:---|\n")
      for id in ab.doc.nodes.keys() {
        match ab.doc.nodes.get(id) {
          Some(n) =>
            if id != "root" {
              // fill
              if n.style.fill != "none" && n.style.fill != "" {
                let tn = match_token_color(tokens, n.style.fill)
                sb.write_string(
                  "| \{id} | fill | `\{n.style.fill}` | \{tn} |\n",
                )
              }
              // text_color
              if n.style.text_color != "#111111" && n.style.text_color != "" {
                let tn2 = match_token_color(tokens, n.style.text_color)
                sb.write_string(
                  "| \{id} | text | `\{n.style.text_color}` | \{tn2} |\n",
                )
              }
            }
          None => ()
        }
      }
      sb.write_string("\n")

      // ── 字体标注 ──
      sb.write_string("## Typography\n\n")
      sb.write_string("| Node | Size | Token |\n")
      sb.write_string("|:---|:---|:---|\n")
      for id in ab.doc.nodes.keys() {
        match ab.doc.nodes.get(id) {
          Some(n) => {
            let is_txt = is_text_node(n)
            if is_txt || n.text != "" {
              let size_str = n.style.font_size.to_int().to_string()
              let token = match_font_token(tokens, n.style.font_size)
              sb.write_string("| \{id} | \{size_str}px | \{token} |\n")
            }
          }
          None => ()
        }
      }
      sb.write_string("\n")

      // ── 间距标注 ──
      sb.write_string("## Spacing\n\n")
      sb.write_string("```text\n")
      for id in ab.doc.nodes.keys() {
        match layout.rect_of(id) {
          Some(r) =>
            if id != "root" {
              let x_str = r.x.to_int().to_string()
              let y_str = r.y.to_int().to_string()
              let w_str = r.width.to_int().to_string()
              let h_str = r.height.to_int().to_string()
              sb.write_string(
                "\{id}: x=\{x_str} y=\{y_str} w=\{w_str} h=\{h_str}\n",
              )
            }
          None => ()
        }
      }
      sb.write_string("```\n\n")

      // ── 布局信息 ──
      sb.write_string("## Layout\n\n")
      for id in ab.doc.nodes.keys() {
        match ab.doc.nodes.get(id) {
          Some(n) =>
            if n.layout is Some(_) && id != "root" {
              match n.layout {
                Some(l) => {
                  let dir = match l.dir {
                    Vertical => "column"
                    Horizontal => "row"
                  }
                  let gap = l.gap.to_int().to_string()
                  let pt = l.padding.top.to_int().to_string()
                  let pr = l.padding.right.to_int().to_string()
                  let pb = l.padding.bottom.to_int().to_string()
                  let pl = l.padding.left.to_int().to_string()
                  sb.write_string(
                    "- **\{id}**: flex-direction: \{dir}; gap: \{gap}px; padding: \{pt}px \{pr}px \{pb}px \{pl}px\n",
                  )
                }
                None => ()
              }
            }
          None => ()
        }
      }
      sb.write_string("\n")

      // ── 交互标注 ──
      sb.write_string("## Interactions\n\n")
      let mut has_interactions = false
      for e in self.flows.iter() {
        if e.from == artboard {
          sb.write_string("- **\{e.trigger}** → navigate to `\{e.to}`\n")
          has_interactions = true
        }
      }
      // 空段判断只看本画板是否列出过条目——其他画板的 flow 不代表本画板有交互
      if !has_interactions {
        sb.write_string("_No interactions defined_\n")
      }
      sb.write_string("\n")

      // ── CSS 变量(令牌映射)──
      sb.write_string("## CSS Custom Properties\n\n")
      sb.write_string("```css\n")
      for k in tokens.colors.keys() {
        match tokens.colors.get(k) {
          Some(v) => sb.write_string("  --color-\{k}: \{v};\n")
          None => ()
        }
      }
      for k in tokens.spacing.keys() {
        match tokens.spacing.get(k) {
          Some(v) => sb.write_string("  --spacing-\{k}: \{v.to_int()}px;\n")
          None => ()
        }
      }
      sb.write_string("```\n")

      Ok(sb.to_string())
    }
  }
}

///|
/// 标注摘要(JSON,机器可读)
pub fn Project::spec_json(
  self : Project,
  artboard~ : String,
) -> Result[String, String] {
  match self.artboards.get(artboard) {
    None => Err("unknown_artboard")
    Some(ab) => {
      let layout = solve(ab.doc)
      let items : Array[String] = []
      for id in ab.doc.nodes.keys() {
        match (ab.doc.nodes.get(id), layout.rect_of(id)) {
          (Some(n), Some(r)) =>
            if id != "root" {
              // rect 用合法 JSON 数组值 [x,y,w,h];id/component/fill/text
              // 过 json_escape——消费端 JSON.parse 不再被裸数字花括号/
              // 未转义引号打崩
              let w = r.width.to_int().to_string()
              let h = r.height.to_int().to_string()
              let x = r.x.to_int().to_string()
              let y = r.y.to_int().to_string()
              let fill = json_escape(n.style.fill)
              let comp = json_escape(n.component)
              let txt = json_escape(n.text)
              items.push(
                "{\"id\":\"\{json_escape(id)}\",\"component\":\"\{comp}\",\"rect\":[\{x},\{y},\{w},\{h}],\"fill\":\"\{fill}\",\"text\":\"\{txt}\"}",
              )
            }
          _ => ()
        }
      }
      let j = jstr20(items)
      Ok("[\{j}]")
    }
  }
}

// ---------------------------------------------------------------------------
// 辅助
// ---------------------------------------------------------------------------

///|
fn match_token_color(tokens : DesignTokens, hex : String) -> String {
  for k in tokens.colors.keys() {
    match tokens.colors.get(k) {
      Some(v) => if v == hex { return "`" + k + "`" }
      None => ()
    }
  }
  "custom"
}

///|
fn match_font_token(tokens : DesignTokens, size : Double) -> String {
  for k in tokens.typography.keys() {
    match tokens.typography.get(k) {
      Some(v) => if v == size { return "`" + k + "`" }
      None => ()
    }
  }
  "custom"
}

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

///|
fn is_text_node(n : Node) -> Bool {
  match n.kind {
    Text => true
    _ => false
  }
}