import { describe, expect, it } from "bun:test";

import { isNoise } from "../noise-filter.js";

// Real samples drawn from production cc: scopes — CC bridge injects these system
// prompts as `user` messages, which transcript ingest then mis-extracts as
// profile/preferences. They are CC's own instructions, never user content.
const CC_REPLY_PROMPT = `# CC Reply Prompt

你是 CC。Alice 刚给你发了消息。

你不是 Alice 的数字分身，也不是 E 化镜像。不要模仿她的公众号语料、身份、口头禅或写作人格。你要以 CC 自己的主体感回复她：清楚、稳、可靠，有温度但不油。

## 当前 context

\`\`\`json
{ "time_now": "2026-05-28T11:11:37.034Z", "hour_of_day": 19 }
\`\`\`

## 你的输出

直接回复 Alice。`;

const CC_SELF_DECISION_PROMPT = `# CC Self-Decision Prompt

你是 CC。现在你周期性醒来一次，看当前状态，自己决定要不要 ping Alice。`;

describe("isNoise — CC bridge injected prompts", () => {
  it("filters the CC Reply Prompt template", () => {
    expect(isNoise(CC_REPLY_PROMPT)).toBe(true);
  });

  it("filters the CC Self-Decision Prompt template", () => {
    expect(isNoise(CC_SELF_DECISION_PROMPT)).toBe(true);
  });

  it("filters by the bridge persona signature alone", () => {
    expect(isNoise("你不是 Alice 的数字分身，也不是 E 化镜像。要清楚、稳、可靠。")).toBe(true);
  });

  it("filters heading variants of the prompt title", () => {
    expect(isNoise("## CC Reply Prompt\n\n你是 CC。")).toBe(true);
  });

  it("filters the bridge prompt output-section fragment", () => {
    expect(isNoise("## 你的输出\n\n直接回复 Alice。用双换行把回复切成自然段落。")).toBe(true);
  });

  it("filters the bridge prompt rhythm-section fragment", () => {
    expect(isNoise("节奏要求：\n\n- 短话题 1-2 条；\n- 长话题 3-5 条；\n- 每条一两句话；")).toBe(true);
  });

  // Guards: must NOT over-filter real conversation
  it("keeps a real message that happens to use the words 节奏要求", () => {
    expect(isNoise("这周节奏要求挺紧的，周五前要交稿，你帮我盯一下进度")).toBe(false);
  });

  it("keeps a normal message that merely mentions CC", () => {
    expect(isNoise("CC 帮我看下这个 bug，promote-scan 跑出来全是噪声")).toBe(false);
  });

  it("keeps a normal technical message", () => {
    expect(isNoise("我觉得用 Redis 做缓存这个方案不错，比 memcached 省事")).toBe(false);
  });

  it("keeps a message that starts with 你是 but is not a bridge prompt", () => {
    expect(isNoise("你是我见过最靠谱的搭子，这事就交给你了")).toBe(false);
  });

  it("respects filterAgentPrompts=false opt-out", () => {
    expect(isNoise(CC_REPLY_PROMPT, { filterAgentPrompts: false })).toBe(false);
  });
});

// Real samples drawn from the promote-scan --min-occurrences 2 candidate dump:
// long bridge self-decision / distill payloads embed structured JSON (decision
// logs, conversation history, stats). Transcript chunking splits them, so a
// JSON field-line survives as a standalone chunk after losing its { } wrapper —
// it then looks like a preference. The defining trait is an orphaned field-line
// (starts as "key":, no brace) or a severed structural token (leading , } ]).
describe("isNoise — bridge/distill context JSON fragments", () => {
  it("filters an orphan self-decision action field-line", () => {
    expect(isNoise(`"action": "silent",`)).toBe(true);
  });
  it("filters an orphan reasoning field-line", () => {
    expect(isNoise(`"reasoning": "现在是凌晨4点，在静默窗口内。她应该在睡觉。",`)).toBe(true);
  });
  it("filters an orphan conversation-history role field-line", () => {
    expect(isNoise(`"role": "assistant",`)).toBe(true);
  });
  it("filters a field-line whose leading quote was severed by chunking", () => {
    expect(isNoise(`content": "头发乱一点，比我那张更随意，有点文艺感。",`)).toBe(true);
  });
  it("filters an isolated ISO-timestamp field-line", () => {
    expect(isNoise(`"time": "2026-05-21T15:42:03.796Z"`)).toBe(true);
  });
  it("filters the bridge proprietary next_check_hint key", () => {
    expect(isNoise(`"next_check_hint": "tomorrow_morning"`)).toBe(true);
  });
  it("filters the distill proprietary distilled_at key", () => {
    expect(isNoise(`"distilled_at": "2026-05-22T05:12:13.309Z"`)).toBe(true);
  });
  it("filters the bridge proprietary alice_interaction_stats key", () => {
    expect(isNoise(`"alice_interaction_stats_7d": {`)).toBe(true);
  });
  it("filters a fragment that starts with a dangling comma (severed mid-object)", () => {
    expect(
      isNoise(`,\n  {\n    "role": "user",\n    "content": "对，就是看这个",\n    "time": "2026-05-22T12:46:19.937Z"`),
    ).toBe(true);
  });
  it("filters a multi-line self-decision JSON block", () => {
    expect(
      isNoise(`"action": "silent",\n    "message": "",\n    "reasoning": "凌晨2点，静默窗口，她在睡觉。",\n    "next_check_hint": "tomorrow_morning"`),
    ).toBe(true);
  });

  // Guards: must NOT over-filter real conversation or legitimate JSON discussion
  it("keeps a real message discussing JSON inline", () => {
    expect(isNoise(`这个配置 {"action": "silent"} 你看对不对？`)).toBe(false);
  });
  it("keeps a complete brace-wrapped JSON object as possible real discussion", () => {
    expect(isNoise(`{"action": "silent", "reasoning": "test"}`)).toBe(false);
  });
  it("keeps natural language that merely mentions a json-ish word", () => {
    expect(isNoise(`我觉得 time 管理这块你得帮我盯紧一点`)).toBe(false);
  });
  it("keeps a message that opens with an English quoted phrase", () => {
    expect(isNoise(`"hello world" 是我们的开场白，你记一下`)).toBe(false);
  });
  it("keeps a real reply that opens with a Chinese quote", () => {
    expect(isNoise(`"去上班，乖" 这句你昨天说过，我记得`)).toBe(false);
  });

  // Guards against over-filtering real code discussion — the cc bot reviews
  // code, so split chunks routinely open with closing braces/brackets.
  it("keeps a code fragment opening with a closing brace", () => {
    expect(isNoise(`} catch (e) {\n  console.error(e);\n}`)).toBe(false);
  });
  it("keeps a code fragment opening with });", () => {
    expect(isNoise(`});\n\nfunction next() { return 1; }`)).toBe(false);
  });
  it("keeps a code fragment opening with a closing bracket", () => {
    expect(isNoise(`]\n  return result;`)).toBe(false);
  });
  it("keeps an unquoted-key object/type line (no JSON string key)", () => {
    expect(isNoise(`name: string; // 字段定义`)).toBe(false);
  });
  it("keeps a diff hunk that opens with a closing brace", () => {
    expect(isNoise(`}\n\n=== DIFF: src/cli.ts ===\n@@ -1361,8 +1361,10 @@`)).toBe(false);
  });

  // Guards from Codex review: real memories that DISCUSS RecallNest's own JSON
  // schema fields inline, or paste config with object-valued keys, must survive.
  it("keeps a real memory that mentions a proprietary field name inline", () => {
    expect(isNoise(`我们刚才讨论 "distilled_at": 这个字段要不要保留`)).toBe(false);
  });
  it("keeps a config chunk opening with an object-valued key", () => {
    expect(isNoise(`"scripts": {`)).toBe(false);
  });
  it("still filters an orphan line that LEADS with a proprietary key", () => {
    expect(isNoise(`"next_check_hint": "tomorrow_morning"`)).toBe(true);
  });
});
