///|
/// playground 引擎会话协议(纯 MoonBit,文本进出,无任何宿主依赖)。
///
/// 文本协议:首行为操作名,其后为载荷:
///
/// - `render\n<声明文本>`          → 解析 + Agent 硬门槛 + SVG
/// - `human_move\n\n\n\n<声明文本>` → 人类策略(结构硬 / 视觉软)
///
/// 会话无内部状态:声明文本本身就是状态。任何宿主(CLI、编辑器、未来的
/// 画布进程)都可以用同一协议驱动引擎,引擎对其一无所知。
pub fn version() -> String {
  "moonviz-playground 0.1.0"
}

///|
/// Agent / 渲染入口:违规整体拒绝
pub fn handle(request : String) -> String {
  handle_with_gate(request, @core.AgentGate)
}

///|
/// 人类入口:结构硬、视觉软;成功携带视觉债
pub fn handle_human(request : String) -> String {
  handle_with_gate(request, @core.HumanGate)
}

///|
fn handle_with_gate(request : String, default_gate : @core.Gate) -> String {
  let lines = split_first_line(request)
  match lines.op {
    "render" =>
      match @decl.parse_decl(lines.rest) {
        Err(e) => parse_error_response(e)
        Ok(page) => {
          let violations = page.check()
          match @core.decide(default_gate, violations) {
            @core.Reject(blocking) => block_response("hard_block", blocking)
            @core.Allow(_) =>
              ok_response(page.render(), page.to_decl_text(), 0, [])
          }
        }
      }
    "human_move" =>
      match move_payload(lines.rest) {
        Err(e) => error_payload("protocol_error", e)
        Ok(mv) =>
          match @decl.parse_decl(mv.rest) {
            Err(e) => parse_error_response(e)
            Ok(page) => {
              let moved = page.move_node(id=mv.id, x=mv.x, y=mv.y)
              match moved {
                Err(msg) => error_payload("unknown_node", msg)
                Ok(_) => {
                  let violations = page.check()
                  match @core.decide(@core.HumanGate, violations) {
                    @core.Reject(blocking) =>
                      block_response("structural_block", blocking)
                    @core.Allow(debt) =>
                      ok_response(
                        page.render(),
                        page.to_decl_text(),
                        debt.length(),
                        debt,
                      )
                  }
                }
              }
            }
          }
      }
    other => error_payload("unknown_op", other)
  }
}

///|
struct FirstLine {
  op : String
  rest : String
}

///|
fn split_first_line(request : String) -> FirstLine {
  let mut idx : Int? = None
  let mut pos = 0
  for c in request.iter() {
    match idx {
      Some(_) => ()
      None => if c == '\n' { idx = Some(pos) } else { pos = pos + 1 }
    }
  }
  match idx {
    Some(i) =>
      {
        op: trim_line(request.substring(start=0, end=i)),
        rest: request.substring(start=i + 1),
      }
    None => { op: trim_line(request), rest: "", }
  }
}

///|
struct MovePayload {
  id : String
  x : Double
  y : Double
  rest : String
}

///|
/// human_move 载荷:第 1..3 行 = id / x / y,其余行 = 当前声明文本
fn move_payload(rest : String) -> Result[MovePayload, String] {
  let lines : Array[String] = Array::new()
  let mut buf = StringBuilder::new()
  for c in rest.iter() {
    if c == '\n' {
      lines.push(buf.to_string())
      buf = StringBuilder::new()
    } else {
      buf.write_char(c)
    }
  }
  lines.push(buf.to_string())
  if lines.length() < 4 {
    return Err("human_move 需要 4 段:id / x / y / 声明文本")
  }
  let x : Double = match parse_num(lines[1]) {
    Some(v) => v
    None => return Err("无法解析 x 数值")
  }
  let y : Double = match parse_num(lines[2]) {
    Some(v) => v
    None => return Err("无法解析 y 数值")
  }
  Ok({ id: trim_line(lines[0]), x, y, rest: join_from(lines, 3), })
}

///|
fn join_from(lines : Array[String], from : Int) -> String {
  let sb = StringBuilder::new()
  for i in from.. from {
      sb.write_char('\n')
    }
    sb.write_string(lines[i])
  }
  sb.to_string()
}

///|
fn trim_line(s : String) -> String {
  match s.find_by(fn(c) { c != ' ' }) {
    None => ""
    Some(a) => {
      let mut b = s.length() - 1
      let mut end = a
      let mut running = true
      while b >= a && running {
        match s.get_char(b) {
          Some(c) =>
            if c != ' ' {
              end = b
              running = false
            } else {
              b = b - 1
            }
          None => running = false
        }
      }
      s.substring(start=a, end=end + 1)
    }
  }
}

///|
/// 正/负整数与小数(坐标域)
fn parse_num(s : String) -> Double? {
  let t : String = trim_line(s)
  if t.length() == 0 {
    return None
  }
  let is_neg : Bool = t.get_char(0) == Some('-')
  let body : String = if is_neg { t.substring(start=1) } else { t }
  let dot : Int? = body.find(".")
  let intp : String = match dot {
    Some(i) => body.substring(start=0, end=i)
    None => body
  }
  let fracp : String = match dot {
    Some(i) => body.substring(start=i + 1)
    None => "0"
  }
  if intp.length() == 0 && body.find(".") != Some(0) {
    return None
  }
  let mut acc = 0
  for c in intp.iter() {
    let code = c.to_int()
    if code < 48 || code > 57 {
      return None
    }
    acc = acc * 10 + (code - 48)
  }
  let mut frac = 0.0
  let mut scale = 0.1
  for c in fracp.iter() {
    let code = c.to_int()
    if code < 48 || code > 57 {
      return None
    }
    frac = frac + (code - 48).to_double() * scale
    scale = scale / 10.0
  }
  let v = acc.to_double() + frac
  Some(if is_neg { -v } else { v })
}

// ---------------------------------------------------------------------------
// 响应序列化(JSON 文本)
// ---------------------------------------------------------------------------

///|
fn join_comma(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 ok_response(
  svg : String,
  decl : String,
  debt : Int,
  violations : Array[@core.Violation],
) -> String {
  let parts : Array[String] = Array::new()
  for v in violations.iter() {
    parts.push(v.to_json())
  }
  "{\n  \"ok\": true,\n  \"debt\": \{debt},\n  \"violations\": [\{join_comma(parts)}],\n  \"decl\": \"\{@core.json_escape(decl)}\",\n  \"svg\": \"\{@core.json_escape(svg)}\"\n}"
}

///|
fn parse_error_response(e : @decl.DeclParseError) -> String {
  "{\"error\": \"parse_error\", \"line\": \{e.line}, \"message\": \"\{@core.json_escape(e.message)}\"}"
}

///|
fn error_payload(kind : String, detail : String) -> String {
  "{\"error\": \"\{kind}\", \"detail\": \"\{@core.json_escape(detail)}\"}"
}

///|
fn block_response(kind : String, blocking : Array[@core.Violation]) -> String {
  let parts : Array[String] = Array::new()
  for v in blocking.iter() {
    parts.push(v.to_json())
  }
  "{\"error\": \"\{kind}\", \"violations\": [\{join_comma(parts)}]}"
}