// mooncassette/core —— 请求规范化。
//
// 规范化只做一件事:**剔除不影响语义、但每次调用都可能变化的字段**。
// 刻意的设计取舍:
// - 不在这里排序对象键。键序归一由 `@canon` 的规范文本负责,
//   这样规范化是幂等的,且与「如何序列化」解耦。
// - 不做模糊匹配。模糊/子集匹配是 `@matcher` 的职责,属于「策略」而非「规范化」。

///|
/// 默认剔除的易变字段名。
///
/// 这些字段在真实 API 请求中经常变化,却不影响语义;
/// 若它们参与指纹,回放将永远无法命中。
pub let default_drop_keys : Array[String] = [
  "request_id", "requestId", "stream_id", "timestamp", "created", "created_at", "nonce",
]

///|
/// 递归剔除对象中的指定键。
///
/// 数组元素会被逐项处理;标量原样返回。
pub fn prune_json(value : Json, drop_keys : ArrayView[String]) -> Json {
  match value {
    Object(fields) => {
      let kept : Map[String, Json] = Map([])
      for key in fields.keys() {
        if !contains_key(drop_keys, key) {
          match fields.get(key) {
            Some(child) => kept.set(key, prune_json(child, drop_keys))
            None => ()
          }
        }
      }
      Json::object(kept)
    }
    Array(items) => {
      let kept : Array[Json] = []
      for item in items {
        kept.push(prune_json(item, drop_keys))
      }
      Json::array(kept)
    }
    scalar => scalar
  }
}

///|
/// 规范化一个请求:剔除易变字段。
///
/// 未显式传入 `drop_keys` 时使用 `default_drop_keys`。
pub fn Request::normalize(
  self : Request,
  drop_keys? : ArrayView[String],
) -> Request {
  let keys = match drop_keys {
    Some(keys) => keys
    None => default_drop_keys[:]
  }
  {
    provider: self.provider,
    model: self.model,
    body: prune_json(self.body, keys),
  }
}

///|
/// 在键名列表中查找目标键(精确匹配,区分大小写)。
fn contains_key(keys : ArrayView[String], target : String) -> Bool {
  for i = 0; i < keys.length(); i = i + 1 {
    if keys[i] == target {
      return true
    }
  }
  false
}