// 精确去重:同 user + 内容指纹(归一化后哈希)。
// 完全重复的事实不重复入库,跳过并返回已存在条目 id。
// 归一化含主语前缀/尾标点折叠,吸收 LLM 提取常见的「我/用户」措辞漂移。

///|
/// 归一化内容(去首尾空白 + 小写 + 主语前缀/尾标点)的内容指纹(FNV-1a)。
/// Package-private; host path goes through MemoryStore::add dedup.
fn content_fingerprint(content : String) -> Int32 {
  fnv1a(normalize_content(content))
}

///|
/// 归一化:trim → lower → 剥常见主语前缀(LLM 提取常在「我/用户」间摇摆)→ 去尾标点。
/// 使「我的工号是 A1024」与「用户的工号是 A1024」落到同一指纹。
fn normalize_content(s : String) -> String {
  let lowered = s.trim().to_owned().to_lower()
  strip_trailing_punct(strip_subject_prefix(lowered))
}

///|
/// 剥句首主语前缀(长前缀优先):用户的 / 用户 / 我的 / 我。
fn strip_subject_prefix(s : String) -> String {
  let prefixes = ["用户的", "用户", "我的", "我"]
  for p in prefixes {
    if s.has_prefix(p) {
      match s.strip_prefix(p) {
        Some(rest) => return rest.trim_start().to_owned()
        None => ()
      }
    }
  }
  s
}

///|
/// 去掉末尾常见中英文标点,避免「……A1024」与「……A1024。」指纹分叉。
fn strip_trailing_punct(s : String) -> String {
  fn is_punct(c : Char) -> Bool {
    c == '.' ||
    c == '!' ||
    c == '?' ||
    c == ',' ||
    c == ';' ||
    c == ':' ||
    c == '。' ||
    c == '!' ||
    c == '?' ||
    c == ',' ||
    c == ';' ||
    c == ':'
  }
  let chars : Array[Char] = []
  for c in s.iter() {
    chars.push(c)
  }
  while chars.length() > 0 && is_punct(chars[chars.length() - 1]) {
    chars.remove(chars.length() - 1) |> ignore
  }
  let buf = StringBuilder()
  for c in chars {
    buf.write_char(c)
  }
  buf.to_string()
}

///|
/// 按 user 分片的内容指纹索引:user → fingerprint → entry_id。
/// 只登记当前可检索(Active / Unstructured)的条目;
/// supersede / forget 时同步摘除。
/// Package-private (abstract): host API is MemoryStore only.
struct DedupIndex {
  by_user : Map[String, Map[Int32, String]]
}

///|
fn DedupIndex::new() -> DedupIndex {
  DedupIndex::{ by_user: Map([]), }
}

///|
/// 查询同 user 是否已存在相同内容指纹的条目;返回已存在条目 id。
fn DedupIndex::lookup(
  self : DedupIndex,
  user_id : String,
  content : String,
) -> String? {
  match self.by_user.get(user_id) {
    Some(fp_map) => fp_map.get(content_fingerprint(content))
    None => None
  }
}

///|
/// 登记指纹 → entry_id。
fn DedupIndex::register(
  self : DedupIndex,
  user_id : String,
  content : String,
  id : String,
) -> Unit {
  let fp = content_fingerprint(content)
  let fp_map = match self.by_user.get(user_id) {
    Some(m) => m
    None => {
      let m : Map[Int32, String] = Map([])
      self.by_user.set(user_id, m)
      m
    }
  }
  fp_map.set(fp, id)
}

///|
/// 摘除指纹(条目被 supersede / forget 时调用)。
fn DedupIndex::unregister(
  self : DedupIndex,
  user_id : String,
  content : String,
) -> Unit {
  match self.by_user.get(user_id) {
    Some(fp_map) => fp_map.remove(content_fingerprint(content))
    None => ()
  }
}