#!/usr/bin/env node
/**
 * WP-WX-01 离线回归：forwardMessage 文本转发真 Frida 分支逻辑
 *
 * 真机铁律：本测试**仅作离线回归**，不替代真机 Frida attach E2E。
 * 它从 wechat_hook_v2.js 抽取真实 forwardMessage 源码（不复制、不漂移），
 * 注入 mock 依赖，验证三条分支：
 *   1) 文本类(type=1) → _sendMessageInternal 真发送成功 → 返回 text_resend，不走 intent
 *   2) 非文本 / DB miss → 降级 _intentAction，标 non_text_or_db_miss（不伪造成功）
 *   3) 真发送未验证(success:false) → 不假成功，降级 intent
 *   4) 缺参 → 直接 error
 *
 * 运行：node sdk/tests/test_forward_message_logic.mjs
 */
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import path from 'node:path';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const JS_PATH = path.join(__dirname, '..', 'agent', 'hook', 'wechat_hook_v2.js');
const src = readFileSync(JS_PATH, 'utf8');

// 抽取 forwardMessage 函数体（花括号配平），避免复制造成漂移
function extractFunc(name) {
  const re = new RegExp(name + '\\s*:\\s*function\\s*\\([^)]*\\)\\s*\\{');
  const m = re.exec(src);
  if (!m) throw new Error('未找到函数 ' + name);
  const bodyStart = src.indexOf('{', m.index + m[0].length - 1);
  let depth = 0;
  for (let i = bodyStart; i < src.length; i++) {
    if (src[i] === '{') depth++;
    else if (src[i] === '}') {
      depth--;
      if (depth === 0) {
        // 返回 "function(params){ ... }" 形式
        const argsStart = src.indexOf('(', m.index);
        const argsEnd = src.indexOf(')', argsStart);
        const args = src.slice(argsStart + 1, argsEnd);
        return new Function(args, src.slice(bodyStart + 1, i));
      }
    }
  }
  throw new Error('花括号未配平');
}

let assertions = 0;
function assert(cond, msg) {
  assertions++;
  if (!cond) { console.error('  ✗ FAIL: ' + msg); process.exitCode = 1; }
  else console.log('  ✓ ' + msg);
}

// ---- mock harness ----
let calls;
function resetMocks(dbRows, sendResult, revokeResult) {
  calls = { execSQL: [], sendInternal: [], intent: [], event: [], revoke: [] };
  globalThis.Java = { performNow: (fn) => fn() };
  globalThis._execSQL = (sql) => { calls.execSQL.push(sql); return dbRows; };
  globalThis._sendMessageInternal = (toId, content, type) => {
    calls.sendInternal.push({ toId, content, type });
    return sendResult;
  };
  globalThis._intentAction = (action, payload, evt, extra) => {
    calls.intent.push({ action, payload, evt, extra });
    return { success: false, action, _intent: true, extra };
  };
  // revokeMessage 已改走真 Frida RevokeMsgEvent，mock 其返回以验证诚实分支
  globalThis._revokeMessageByRevokeMsgEvent = (msgSvrId, row) => {
    calls.revoke.push({ msgSvrId, row });
    return revokeResult || { success: false, error: 'revoke_frida_failed', msg_svr_id: msgSvrId, eligible: true };
  };
  globalThis.emitEvent = (evt, data) => { calls.event.push({ evt, data }); };
  globalThis.String = String;
}

const forwardMessage = extractFunc('forwardMessage');

console.log('[WP-WX-01] forwardMessage 离线分支回归');

// 1) 文本类真发送成功
resetMocks([{ type: 1, content: '你好转发' }], { success: true, message_id: 'm123', verified: true });
let r = forwardMessage({ msg_svr_id: 's1', to_id: 'wxid_a' });
assert(r.success === true, '文本类转发成功');
assert(r.forward_kind === 'text_resend', '标记 text_resend');
assert(calls.sendInternal.length === 1 && calls.sendInternal[0].content === '你好转发', '走 _sendMessageInternal 真发送');
assert(calls.intent.length === 0, '成功路径不降级 intent');
assert(calls.event.length === 1 && calls.event[0].evt === 'message_forwarded', '发 message_forwarded 事件');

// 2) 非文本（type=3 图片）→ intent 降级，不假成功
resetMocks([{ type: 3, content: '[图片]' }], { success: true });
r = forwardMessage({ msg_svr_id: 's2', to_id: 'wxid_b' });
assert(calls.sendInternal.length === 0, '非文本不调真发送');
assert(calls.intent.length === 1 && calls.intent[0].extra.kind === 'non_text_or_db_miss', '非文本降级 intent (non_text_or_db_miss)');
assert(r._intent === true && r.success === false, '降级 intent 不伪造成功');

// 3) DB miss（无行）→ intent 降级
resetMocks([], { success: true });
r = forwardMessage({ msg_svr_id: 's3', to_id: 'wxid_c' });
assert(calls.intent.length === 1, 'DB miss 降级 intent');

// 4) 文本但真发送未验证(success:false) → 不假成功，降级 intent
resetMocks([{ type: 1, content: 'x' }], { success: false, error: 'unverified' });
r = forwardMessage({ msg_svr_id: 's4', to_id: 'wxid_d' });
assert(calls.sendInternal.length === 1, '尝试真发送');
assert(calls.intent.length === 1, '真发送未验证 → 降级 intent，不假成功');

// 5) 缺参
r = forwardMessage({ msg_svr_id: '', to_id: '' });
assert(r.success === false && /缺少/.test(r.error), '缺参直接 error');

// ---- forwardMultiple 批量转发 ----
console.log('[WP-WX-01] forwardMultiple 离线分支回归');
const forwardMultiple = extractFunc('forwardMultiple');

// 6) 全文本 → 全部真发送，forwarded=2
resetMocks([{ type: 1, content: 'a' }], { success: true, message_id: 'm' });
r = forwardMultiple({ msg_svr_ids: ['s1', 's2'], to_id: 'wxid_x' });
assert(r.success === true && r.forwarded === 2 && r.fallback === 0, '全文本批量转发 forwarded=2');
assert(calls.sendInternal.length === 2, '逐条调真发送 2 次');
assert(calls.intent.length === 0, '有成功条不整体降级');

// 7) 全非文本 → forwarded=0 整体降级 intent，不假成功
resetMocks([{ type: 3, content: '[图片]' }], { success: true });
r = forwardMultiple({ msg_svr_ids: ['s1', 's2'], to_id: 'wxid_y' });
assert(r._intent === true && r.success === false, '全非文本整体降级 intent 不假成功');
assert(calls.intent[0].extra.forwarded === 0, 'intent 标 forwarded=0');

// 8) 缺参
r = forwardMultiple({ msg_svr_ids: [], to_id: 'wxid_z' });
assert(r.success === false && /缺少/.test(r.error), 'forwardMultiple 缺参 error');

// ---- revokeMessage 撤回资格预检 ----
console.log('[WP-WX] revokeMessage 资格预检回归');
const revokeMessage = extractFunc('revokeMessage');

// 9) 消息不存在 → 诚实 msg_not_found，不发 intent
resetMocks([], null);
r = revokeMessage({ msg_svr_id: 's1' });
assert(r.success === false && r.error_code === 'msg_not_found', '消息不存在→msg_not_found');
assert(calls.intent.length === 0, '不存在不发 intent 假成功');

// 10) 非本人发送 → not_own_message
resetMocks([{ isSend: 0, createTime: Date.now() }], null);
r = revokeMessage({ msg_svr_id: 's2' });
assert(r.success === false && r.error_code === 'not_own_message', '非本人→not_own_message');

// 11) 超 2 分钟窗口 → revoke_window_expired
resetMocks([{ isSend: 1, createTime: Date.now() - 200000 }], null);
r = revokeMessage({ msg_svr_id: 's3' });
assert(r.success === false && r.error_code === 'revoke_window_expired', '超窗→revoke_window_expired');

// 12) 资格通过(本人+窗口内) → 走真 Frida RevokeMsgEvent，DB 已确认→成功发 message_revoked
resetMocks([{ isSend: 1, createTime: Date.now() - 5000 }], null, { success: true, verified: true, msg_svr_id: 's4' });
r = revokeMessage({ msg_svr_id: 's4' });
assert(calls.revoke.length === 1, '资格通过→走真 Frida RevokeMsgEvent');
assert(r.success === true && r.verified === true, '真撤回 DB 确认→成功');
assert(calls.event.length === 1 && calls.event[0].evt === 'message_revoked', '发 message_revoked 事件');

// 12b) 资格通过但真 RPC 未 DB 确认 → 诚实失败，不假成功、不发事件
resetMocks([{ isSend: 1, createTime: Date.now() - 5000 }], null, { success: false, error: 'revoke_frida_failed', eligible: true });
r = revokeMessage({ msg_svr_id: 's4b' });
assert(calls.revoke.length === 1, '资格通过→尝试真 Frida 撤回');
assert(r.success === false && calls.event.length === 0, '真撤回未确认→诚实失败不发事件');

// 13) 缺参
r = revokeMessage({ msg_svr_id: '' });
assert(r.success === false && /缺少/.test(r.error), 'revokeMessage 缺参 error');

console.log(`\n[done] 断言 ${assertions} 条，exit=${process.exitCode || 0}`);
