// mooncassette/format_lock —— 生成 FORMAT.lock:0.10.0 的格式快照。
//
// 这是「格式冻结」机制的执行端:用当前写入端构造一组覆盖格式各个角落的
// 见证 cassette,把它们的规范文本、缩进文本与摘要打印到标准输出。CI 会把
// 这份输出与仓库里的 FORMAT.lock 逐字节比对,于是任何解析 / 序列化行为的
// 改动都会立刻现形。
//
// 生成方式(在仓库根目录):
//     moon run examples/format_lock --target js
//
// 程序直接写出 FORMAT.lock(经由 `x/fs`,逐字节写 UTF-8、`\n` 行尾),
// 而不是打印到标准输出再由 shell 重定向 —— shell 的编码与行尾策略在
// Windows / Linux 上并不一致,会让「同一份源码」产出两份字节不同的快照。
//
// 格式是被承诺过的契约:改动它必须显式重新生成本文件,并在评审里说明缘由
// 以及对读端兼容性的影响。

///|
/// 见证样例使用的固定 generator。
///
/// 刻意不用 `@core.generator_id`:否则每发一版都会改动本文件,把「格式变了」
/// 与「版本号变了」搅在一起。版本号与 generator 的一致性由 CI 的版本校验
/// 步骤单独负责。
let witness_generator : String = "mooncassette/format-lock"

///|
fn meta_of(name : String, recorded_at? : String) -> @core.CassetteMeta {
  { name, generator: witness_generator, recorded_at }
}

///|
fn json_obj(entries : Array[(String, Json)]) -> Json {
  let map : Map[String, Json] = Map([])
  for entry in entries {
    map.set(entry.0, entry.1)
  }
  Json::object(map)
}

///|
fn json_arr(items : Array[Json]) -> Json {
  Json::array(items)
}

///|
fn json_str(text : String) -> Json {
  Json::string(text)
}

///|
/// 构造一个「看起来像整数」的 JSON 数字,避免写出 `42.0` 这类噪声。
fn json_int(value : Int) -> Json {
  Json::number(value.to_double(), repr=value.to_string())
}

// ---------------------------------------------------------------------------
// 见证样例
// ---------------------------------------------------------------------------

///|
/// 见证 1:版本 1 的非流式形状(0.3.0 及更早的写法)。
///
/// 覆盖:缺省的 `usage` / `stream` 被省略、旧版本声明原样写出(不擅自升版)。
fn witness_v1() -> @core.Cassette {
  let request : @core.Request = {
    provider: "openai",
    model: "gpt-4o",
    body: json_obj([
      (
        "messages",
        json_arr([
          json_obj([("role", json_str("user")), ("content", json_str("ping"))]),
        ]),
      ),
    ]),
  }
  let response : @core.Response = {
    status: 200,
    body: json_obj([
      (
        "choices",
        json_arr([
          json_obj([
            (
              "message",
              json_obj([
                ("role", json_str("assistant")),
                ("content", json_str("pong")),
              ]),
            ),
          ]),
        ]),
      ),
    ]),
    usage: None,
    stream: None,
  }
  {
    version: 1,
    meta: meta_of("witness-v1"),
    interactions: [{ request, response }],
  }
}

///|
/// 见证 2:版本 2 + `usage` + `recorded_at`。
///
/// 覆盖:`usage` 对象、整数写法(不出现 `12.0`)、可选的 `recorded_at`。
fn witness_usage() -> @core.Cassette {
  let request : @core.Request = {
    provider: "anthropic",
    model: "claude-3-5-sonnet",
    body: json_obj([
      ("max_tokens", json_int(64)),
      (
        "messages",
        json_arr([
          json_obj([("role", json_str("user")), ("content", json_str("你好"))]),
        ]),
      ),
    ]),
  }
  let response : @core.Response = {
    status: 200,
    body: json_obj([
      (
        "content",
        json_arr([
          json_obj([
            ("type", json_str("text")),
            ("text", json_str("你好!我是月兔助手。")),
          ]),
        ]),
      ),
    ]),
    usage: Some(@core.Usage::new(11, 7)),
    stream: None,
  }
  {
    version: 2,
    meta: meta_of("witness-usage", recorded_at="2026-01-01T00:00:00Z"),
    interactions: [{ request, response }],
  }
}

///|
/// 见证 3:流式响应(版本 2 引入的 `stream` 帧序列)。
///
/// 覆盖:带 / 不带 `event` 的帧、`[DONE]` 哨兵、被转义后嵌入的帧文本。
fn witness_stream() -> @core.Cassette {
  let request : @core.Request = {
    provider: "openai",
    model: "gpt-4o",
    body: json_obj([
      ("stream", Json::boolean(true)),
      (
        "messages",
        json_arr([
          json_obj([
            ("role", json_str("user")),
            ("content", json_str("stream please")),
          ]),
        ]),
      ),
    ]),
  }
  let frames : Array[@core.StreamFrame] = [
    @core.StreamFrame::new(
      "{\"choices\":[{\"delta\":{\"content\":\"hi\"},\"index\":0}]}",
      event="message",
    ),
    @core.StreamFrame::new("[DONE]"),
  ]
  let response : @core.Response = {
    status: 200,
    body: json_obj([("choices", json_arr([]))]),
    usage: Some(@core.Usage::new(3, 2)),
    stream: Some(frames),
  }
  {
    version: 2,
    meta: meta_of("witness-stream"),
    interactions: [{ request, response }],
  }
}

///|
/// 见证 4:二进制载荷信封。
///
/// 覆盖:`payload` + `content_encoding` 的规范形状,含需要补齐填充的字节
/// (`café` 为 5 字节)与空字节串。
fn witness_envelope() -> @core.Cassette {
  let padded = @payload.pack_bytes(@utf8.encode("café"))
  let empty = @payload.pack_bytes(Bytes::new(0))
  let request : @core.Request = {
    provider: "openai",
    model: "gpt-4o",
    body: json_obj([("input", padded)]),
  }
  let response : @core.Response = {
    status: 200,
    body: json_obj([("output", empty)]),
    usage: None,
    stream: None,
  }
  {
    version: 2,
    meta: meta_of("witness-envelope"),
    interactions: [{ request, response }],
  }
}

///|
/// 见证 5:文本转义与数字写法。
///
/// 覆盖:引号、反斜杠、换行、制表、控制字符、非 ASCII、补充平面字符(emoji)、
/// BOM,以及浮点 / 负整数 / 零 / 千级整数。
fn witness_escapes() -> @core.Cassette {
  let tricky = "q:\" bs:\\ nl:\n tab:\t ctrl:\u{01} 中文 😀 \u{FEFF}bom"
  let request : @core.Request = {
    provider: "openai",
    model: "gpt-4o",
    body: json_obj([("text", json_str(tricky))]),
  }
  let response : @core.Response = {
    status: 200,
    body: json_obj([
      ("text", json_str(tricky)),
      ("float", Json::number(3.5)),
      ("negative", json_int(-7)),
      ("zero", json_int(0)),
      ("thousand", json_int(1000)),
    ]),
    usage: None,
    stream: None,
  }
  {
    version: 2,
    meta: meta_of("witness-escapes"),
    interactions: [{ request, response }],
  }
}

// ---------------------------------------------------------------------------
// 输出
// ---------------------------------------------------------------------------

///|
fn header_lines() -> Array[String] {
  [
    "# mooncassette — FORMAT.lock",
    "#",
    "# 这是 mooncassette 的格式快照(0.10.0 起冻结),由当前写入端生成。CI 会重新",
    "# 生成并与本文件逐字节比对,因此任何解析 / 序列化行为的改动都会在这里现形。",
    "#",
    "# 生成方式(在仓库根目录):",
    "#     moon run examples/format_lock --target js",
    "#",
    "# 格式是被承诺过的契约:改它必须显式重新生成本文件,并在评审里说明缘由",
    "# 以及对读端兼容性的影响。",
    "#",
    "# 见证样例刻意使用固定 generator(" +
    witness_generator +
    "),使本文件只随格式变化,",
    "# 不随发布版本号变化。",
    "",
    "## constants",
    "format_tag=" + @codec.format_tag,
    "cassette_format_version=" + @core.cassette_format_version.to_string(),
    "stream_format_version=" + @core.stream_format_version.to_string(),
    "content_encoding=" + @payload.encoding_base64,
    "payload_key=" + @payload.payload_key,
    "content_encoding_key=" + @payload.content_encoding_key,
    "min_payload_bytes=" + @payload.min_payload_bytes.to_string(),
    "min_payload_chars=" + @payload.min_payload_chars.to_string(),
    "fingerprint_algorithm=" + @fingerprint.algorithm,
  ]
}

///|
/// 见证样例清单。集中在一处,便于审阅「快照到底覆盖了什么」。
fn witnesses() -> Array[(String, @core.Cassette)] {
  [
    ("v1-nonstream", witness_v1()),
    ("v2-usage-recorded", witness_usage()),
    ("v2-stream", witness_stream()),
    ("v2-envelope", witness_envelope()),
    ("v2-escapes", witness_escapes()),
  ]
}

///|
/// 自校验:某种文本必须能原样解回同一个 cassette。
///
/// 生成器自己先证明「写出去的东西读得回来」,这份快照才有资格被当作契约。
fn check_text(
  name : String,
  label : String,
  text : String,
  cassette : @core.Cassette,
) -> Bool {
  try {
    let restored = @codec.decode(text)
    if restored == cassette {
      true
    } else {
      println(
        "format_lock: 见证 " +
        name +
        " 的 " +
        label +
        " 文本往返后不等价",
      )
      println("  before: " + @codec.encode(cassette, indent=0))
      println("  after : " + @codec.encode(restored, indent=0))
      false
    }
  } catch {
    error => {
      println(
        "format_lock: 见证 " +
        name +
        " 的 " +
        label +
        " 文本解码报错:" +
        error.to_string(),
      )
      false
    }
  }
}

///|
/// 自校验:规范文本与缩进文本都必须能原样解回同一个 cassette。
fn check_roundtrip(name : String, cassette : @core.Cassette) -> Unit {
  let canonical = @codec.encode(cassette, indent=0)
  let pretty = @codec.encode(cassette, indent=2)
  let canonical_ok = check_text(name, "canonical", canonical, cassette)
  let pretty_ok = check_text(name, "pretty", pretty, cassette)
  if !canonical_ok || !pretty_ok {
    panic()
  }
}

///|
/// 生成 FORMAT.lock 的完整文本。
///
/// 每行都以 `\n` 结尾(不依赖平台行尾),因此这份文件在任何系统上都逐字节
/// 一致。
fn lock_text() -> String {
  let lines : Array[String] = header_lines()
  for entry in witnesses() {
    let name = entry.0
    let cassette = entry.1
    check_roundtrip(name, cassette)
    lines.push("")
    lines.push("## witness: " + name)
    for item in cassette.interactions {
      lines.push("fingerprint=" + @fingerprint.fingerprint(item.request))
      lines.push("integrity=" + @fingerprint.integrity_digest(item))
    }
    lines.push("--- canonical ---")
    lines.push(@codec.encode(cassette, indent=0))
    lines.push("--- pretty ---")
    lines.push(@codec.encode(cassette, indent=2))
  }
  let mut text : String = ""
  for line in lines {
    text = text + line + "\n"
  }
  text
}

///|
/// 写出 FORMAT.lock(路径相对当前工作目录,请在仓库根目录运行)。
fn main {
  let text = lock_text()
  @fs.write_string_to_file("FORMAT.lock", text) catch {
    @fs.IOError(message) => {
      println("format_lock: 写 FORMAT.lock 失败:" + message)
      panic()
    }
  }
  println(
    "format_lock: 已写出 FORMAT.lock,共 " +
    witnesses().length().to_string() +
    " 个见证样例",
  )
}