/**
 * 无障碍文本解析器测试。
 *
 * 重点验证：
 * - 银行详情页（兴业/建行/招行等）通用解析器，按页面特征词分派
 * - 金额格式兼容性：¥ 前缀、千分位逗号、符号前置/后置负号
 * - 不破坏支付宝/微信现有解析行为
 *
 * 测试样本取自真实无障碍日志（兴业/建行/支付宝详情页节点文本）。
 */
import { describe, it, expect } from 'vitest';
import { parseAccessibilityTexts } from '../src/services/automation/accessibilityParser';

// ─── 真实样本（取自日志） ───────────────────────────────────────

/** 兴业银行交易明细详情页（日志 14:45:49，支出 49.00）。 */
const CIB_TEXTS = [
  '交易明细详情', '支出金额', '¥49.00', '对方信息', '1100000000', '对方银行',
  '财付通支付科技有限公司', '交易摘要', '跨行无卡消费', '交易时间', '2026/07/17 20:29:48',
  '记账日期', '2026/07/17', '用途', '财付通(银联云闪付)', '账户余额', '11,565.20 ', '',
];

/** 建设银行明细详情页（日志 14:47:57，还款 -12.32，京东白条）。 */
const CCB_TEXTS = [
  '明细详情', 'png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8BAM', '还款', '¥-12.32',
  '交易账户', '湘潭大学 6217***9959', '交易时间', '2026-07-22 11:32:06',
  '交易地点/附言', '京东支付-京东金融-京东白条及金条业务网银平台', '对方账户',
  '京***及金条业务网银平台 Z******0011', '记账日', '2026-07-22', '账户余额', '¥ 0.79', '备注',
];

/** 支付宝账单详情页（日志 14:48:23，花呗消费 66.8）。 */
const ALIPAY_TEXTS = [
  '盛香亭新式热卤', '支出66.8元', '交易成功', '支付时间', '2026-07-24 21:53:15',
  '2026-07-24 21:53:15', '付款方式', '花呗 ', '商品说明', '湘江新区闪亮云顶餐饮店',
  '湘江新区闪亮云顶餐饮店', '账单分类', '餐饮美食', '账单详情',
];

// ─── 银行解析器 ─────────────────────────────────────────────

describe('parseBankTexts（通用银行解析器）', () => {
  it('解析兴业银行详情页：金额带 ¥ 前缀，方向为支出', () => {
    const event = parseAccessibilityTexts(CIB_TEXTS, 'com.cib.cibmb');
    expect(event).not.toBeNull();
    expect(event!.amount).toBe('49.00');
    expect(event!.currency).toBe('CNY');
    expect(event!.direction).toBe('expense');
    expect(event!.occurredAt).toBe('2026/07/17 20:29:48');
  });

  it('解析兴业银行详情页：提取对手方（被动扣款通知无持卡账户）', () => {
    const event = parseAccessibilityTexts(CIB_TEXTS, 'com.cib.cibmb');
    expect(event).not.toBeNull();
    // 对手方：对方信息下一节点为手机号（纯数字被跳过），回退到对方银行或交易摘要
    expect(event!.counterparty.length).toBeGreaterThan(0);
    // 兴业这类被动扣款通知没有"交易账户"标签，付款方式从包名友好名兜底
    // （com.cib.cibmb → "兴业银行"），避免落入 Assets:Unknown 兜底
    expect(event!.raw['收/付款方式']).toBe('兴业银行');
  });

  it('解析建设银行还款页：金额 ¥-12.32（符号前置负号）', () => {
    const event = parseAccessibilityTexts(CCB_TEXTS, 'com.chinamworld.main');
    expect(event).not.toBeNull();
    expect(event!.amount).toBe('12.32');
    expect(event!.direction).toBe('expense');
    expect(event!.occurredAt).toBe('2026-07-22 11:32:06');
  });

  it('付款方式缺省时从包名友好名兜底（建行 → 建设银行）', () => {
    // 建行这条有"交易账户"标签会优先提取，这里构造一个无任何付款方式标签的银行详情页
    const texts = [
      '明细详情', '支出金额', '¥100.00', '对方信息', '某商户',
      '交易时间', '2026-07-24 10:00:00', '记账日', '2026-07-24', '账户余额', '¥ 500.00',
    ];
    const event = parseAccessibilityTexts(texts, 'com.chinamworld.main');
    expect(event).not.toBeNull();
    expect(event!.raw['收/付款方式']).toBe('建设银行');
  });

  it('解析建设银行还款页：备注包含"还款"文案（供还款识别规则命中）', () => {
    const event = parseAccessibilityTexts(CCB_TEXTS, 'com.chinamworld.main');
    expect(event).not.toBeNull();
    expect(event!.memo).toContain('还款');
  });

  it('解析建设银行还款页：交易账户作为付款方式写入 raw', () => {
    const event = parseAccessibilityTexts(CCB_TEXTS, 'com.chinamworld.main');
    expect(event).not.toBeNull();
    expect(event!.raw['收/付款方式']).toContain('湘潭大学');
  });

  it('跳过 base64 图片节点，不误识别为金额或对手方', () => {
    const event = parseAccessibilityTexts(CCB_TEXTS, 'com.chinamworld.main');
    expect(event).not.toBeNull();
    expect(event!.counterparty).not.toContain('base64');
    expect(event!.amount).not.toContain('base64');
  });

  it('任意包名都能触发银行解析（按页面特征词分派，不依赖包名白名单）', () => {
    // 用一个未登记的包名，但文本含银行详情页特征词，应能解析
    const event = parseAccessibilityTexts(CIB_TEXTS, 'com.unknown.bank.app');
    expect(event).not.toBeNull();
    expect(event!.amount).toBe('49.00');
  });

  it('招行掌银（交易明细 + 带冒号标签 + 无商户标签）：不降级 OCR，商户取金额前节点', () => {
    // 真机日志：招行掌银 ChatGPT 订阅，特征词"交易明细"，标签带冒号，商户名裸露在金额前
    const CMB_TEXTS = [
      '图标', '交易明细', 'GOOGLE *ChatGPT', '¥143.97', '已入账',
      '信用卡:', '3315', '交易时间:', '2026-07-23 00:00:00', '入账日期:', '2026-07-24',
      '国家或地区:', '美国', '交易地:', '交易地金额:', '21.19', '我要分期',
    ];
    const event = parseAccessibilityTexts(CMB_TEXTS, 'com.cmbchina.ccd.pluto.cmbActivity');
    expect(event).not.toBeNull();
    // 金额取带 ¥ 的 143.97（非交易地金额 21.19）
    expect(event?.amount).toBe('143.97');
    // 商户取金额前节点（GOOGLE *ChatGPT），跳过特征词"交易明细"
    expect(event?.counterparty).toBe('GOOGLE *ChatGPT');
    // 时间：含"交易时间"的节点（"交易时间:"）取下一节点
    expect(event?.occurredAt).toContain('2026-07-23');
  });
});

// ─── 金额格式兼容性 ─────────────────────────────────────────

describe('金额格式兼容性', () => {
  const makeTexts = (amountNode: string) => [
    '交易明细详情', '支出金额', amountNode, '对方信息', '某商户',
    '交易时间', '2026-07-24 10:00:00', '记账日期', '2026-07-24',
  ];

  it('支持 ¥ 前缀：¥49.00', () => {
    const event = parseAccessibilityTexts(makeTexts('¥49.00'), 'com.cib.cibmb');
    expect(event?.amount).toBe('49.00');
  });

  it('支持 ￥ 前缀：￥49.00', () => {
    const event = parseAccessibilityTexts(makeTexts('￥49.00'), 'com.cib.cibmb');
    expect(event?.amount).toBe('49.00');
  });

  it('支持符号前置负号：¥-12.32', () => {
    const event = parseAccessibilityTexts(makeTexts('¥-12.32'), 'com.cib.cibmb');
    expect(event?.amount).toBe('12.32');
    expect(event?.direction).toBe('expense');
  });

  it('支持金额前带空格：¥ 0.79', () => {
    const event = parseAccessibilityTexts(makeTexts('¥ 0.79'), 'com.cib.cibmb');
    expect(event?.amount).toBe('0.79');
  });

  it('支持千分位逗号：11,565.20', () => {
    const event = parseAccessibilityTexts(
      ['交易明细详情', '交易摘要', '某消费', '交易金额', '1,234.56', '交易时间', '2026-07-24 10:00:00', '记账日', '2026-07-24'],
      'com.cib.cibmb',
    );
    expect(event?.amount).toBe('1234.56');
  });

  it('不匹配纯整数（避免误吞年份/卡号）：2026', () => {
    const event = parseAccessibilityTexts(
      ['交易明细详情', '支出金额', '2026', '对方信息', '商户', '交易时间', '2026-07-24 10:00:00', '记账日', '2026-07-24'],
      'com.cib.cibmb',
    );
    // 金额无法解析（2026 无小数点），应返回 null
    expect(event).toBeNull();
  });

  it('不误匹配非金额的纯数字小数（无 ¥ 也无千分位时，兜底分支不接管）', () => {
    // "66.8" 是支付宝风格，但此处包名是银行，且无 ¥/千分位，兜底分支应跳过
    // 只有当存在金额标签"支出金额"时才提取
    const event = parseAccessibilityTexts(
      ['交易明细详情', '支出金额', '66.8', '对方信息', '商户', '交易时间', '2026-07-24 10:00:00', '记账日', '2026-07-24'],
      'com.cib.cibmb',
    );
    expect(event?.amount).toBe('66.8');
  });
});

// ─── 不破坏支付宝/微信现有行为 ───────────────────────────────

describe('支付宝/微信解析行为回归（不应被银行解析器干扰）', () => {
  it('支付宝详情页正常解析（包名分派优先）', () => {
    const event = parseAccessibilityTexts(ALIPAY_TEXTS, 'com.eg.android.AlipayGphone');
    expect(event).not.toBeNull();
    expect(event!.amount).toBe('66.8');
    expect(event!.direction).toBe('expense');
    expect(event!.counterparty).toBe('盛香亭新式热卤');
    expect(event!.raw['收/付款方式']).toBe('花呗');
  });

  it('微信详情页正常解析（包名分派优先）', () => {
    const wechatTexts = [
      '某商户', '12.93', '交易单号', '4200001234202407123456789',
      '本服务由财付通提供', '2026-07-24 10:00:00',
    ];
    const event = parseAccessibilityTexts(wechatTexts, 'com.tencent.mm');
    expect(event).not.toBeNull();
    expect(event!.amount).toBe('12.93');
    expect(event!.direction).toBe('expense');
  });

  it('支付宝包名不误触发银行解析器', () => {
    // 支付宝文本不含银行特征词，即便强行走银行分支也应返回 null
    // （实际上包名分派会先走支付宝，这里验证不冲突）
    const event = parseAccessibilityTexts(ALIPAY_TEXTS, 'com.eg.android.AlipayGphone');
    expect(event).not.toBeNull();
    expect(event!.counterparty).toBe('盛香亭新式热卤');
  });
});

// ─── 边界情况 ─────────────────────────────────────────────

describe('边界情况', () => {
  it('空文本返回 null', () => {
    expect(parseAccessibilityTexts([], 'com.cib.cibmb')).toBeNull();
  });

  it('非详情页文本返回 null', () => {
    expect(parseAccessibilityTexts(['随便一些', '不相关', '文本'], 'com.cib.cibmb')).toBeNull();
  });

  it('详情页但金额全为 0 返回 null', () => {
    const event = parseAccessibilityTexts(
      ['交易明细详情', '支出金额', '¥0.00', '对方信息', '商户', '交易时间', '2026-07-24 10:00:00', '记账日', '2026-07-24'],
      'com.cib.cibmb',
    );
    expect(event).toBeNull();
  });

  it('ImportedEvent 必填字段完整', () => {
    const event = parseAccessibilityTexts(CIB_TEXTS, 'com.cib.cibmb');
    expect(event).not.toBeNull();
    expect(event!.id).toBeTruthy();
    expect(event!.occurredAt).toBeTruthy();
    expect(event!.amount).toBeTruthy();
    expect(event!.currency).toBe('CNY');
    expect(event!.raw.packageName).toBe('com.cib.cibmb');
  });
});

describe('parseUnionPayTexts（云闪付交易详情页）', () => {
  // 真机日志实测：云闪付乘车码交易详情页无障碍节点
  // 商户名裸露在 -¥金额 上一节点，付款方式有标签，时间含"延迟扣费"前缀
  const UNIONPAY_TEXTS = [
    '返回', '云闪付交易详情', '客服按钮',
    '长沙公交智慧大数据科技有限公司(乘车码）', '-¥1.16',
    '优惠信息', '绿色出行-天天有优惠（公交地铁）-¥0.24',
    '当前状态', '交易成功',
    '订单金额', '¥1.40',
    '付款方式', '招商银行银联信用卡[1589]',
    '扣款时间', '延迟扣费|2026年6月27日 13:30:15',
    '乘车信息', '8205路 2026年6月27日 13:28:46',
    '交易流水号', '956893083417414766021', '商户订单号', '1388149433202380800',
    '在此商户的交易',
  ];

  it('云闪付详情页：识别金额 -1.16，方向支出', () => {
    const event = parseAccessibilityTexts(UNIONPAY_TEXTS, 'com.unionpay');
    expect(event).not.toBeNull();
    expect(event?.amount).toBe('1.16');
    expect(event?.direction).toBe('expense');
  });

  it('云闪付详情页：商户名取自金额上一节点（含全角括号归一化）', () => {
    const event = parseAccessibilityTexts(UNIONPAY_TEXTS, 'com.unionpay');
    expect(event).not.toBeNull();
    // 全角（）应归一化为半角 ()
    expect(event?.counterparty).toBe('长沙公交智慧大数据科技有限公司(乘车码)');
  });

  it('云闪付详情页：付款方式提取到 raw', () => {
    const event = parseAccessibilityTexts(UNIONPAY_TEXTS, 'com.unionpay');
    expect(event?.raw['收/付款方式']).toBe('招商银行银联信用卡[1589]');
  });

  it('云闪付详情页：时间提取（含延迟扣费前缀清洗）', () => {
    const event = parseAccessibilityTexts(UNIONPAY_TEXTS, 'com.unionpay');
    expect(event).not.toBeNull();
    // 原始 "延迟扣费|2026年6月27日 13:30:15" → occurredAt 含 2026-06-27
    expect(event?.occurredAt).toContain('2026-06-27');
    expect(event?.occurredAt).toContain('13:30');
  });

  it('云闪付列表页（非详情页）返回 null', () => {
    const listTexts = ['返回', '交易记录', '2026年7月', '云闪付APP', '筛选', '07.17 周五', '-¥49.00'];
    const event = parseAccessibilityTexts(listTexts, 'com.unionpay');
    // 列表页无"云闪付交易详情"标题，也无乘车码+金额组合，不应误识别
    expect(event).toBeNull();
  });
});

describe('parseDidiTexts（滴滴出行订单详情页）', () => {
  // 真机日志：节点含粘连长文本（创建时间、订单号等合并成一个节点）
  const DIDO_TEXTS = [
    '返回', '订单详情', '特惠快车打车-方师傅-04月25日行程', '10.80', '付款成功',
    '消费场景特惠快车创建时间2026-04-25 16:24:03订单号212_202604254026406101466527订单编号XY0021308320260425162402GM3bd6付款手机号176****7202',
    '订单金额10.80', '距离更近', '湘江科创基地1期(湘江科创', '园)8栋',
  ];

  it('滴滴：商户取标题"-"前主体（特惠快车打车）', () => {
    const event = parseAccessibilityTexts(DIDO_TEXTS, 'com.sdu.didi.psnger');
    expect(event).not.toBeNull();
    expect(event?.counterparty).toBe('特惠快车打车');
  });

  it('滴滴：金额提取（兼容粘连"订单金额10.80"）', () => {
    const event = parseAccessibilityTexts(DIDO_TEXTS, 'com.sdu.didi.psnger');
    expect(event?.amount).toBe('10.80');
    expect(event?.direction).toBe('expense');
  });

  it('滴滴：从粘连节点提取创建时间', () => {
    const event = parseAccessibilityTexts(DIDO_TEXTS, 'com.sdu.didi.psnger');
    expect(event?.occurredAt).toContain('2026-04-25');
    expect(event?.occurredAt).toContain('16:24');
  });

  it('滴滴非订单页返回 null', () => {
    expect(parseAccessibilityTexts(['返回', '我的行程', '进行中'], 'com.sdu.didi.psnger')).toBeNull();
  });
});

describe('parseMeituanTexts（美团消费详情页）', () => {
  // 真机日志：节点含 base64 图片哈希（混在金额附近，需跳过）
  const MEITUAN_TEXTS = [
    '返回', '消费详情', '牛爷烧土家鲜肉饼', '9.36', '订单状态',
    '3cfaedc4036dbc99467a0afda37ae64f1671', '支付成功', '2026-07-24 18:51:36',
    '月付付款9.36元，计入2026年8月账单，还款日8月15日',
    '468934a59ff36d2e29fd2db3796011bb1188', '美团商品订单', '查看',
    '42846b1ee2ad163a01ba2c8e5462675722729', '牛爷烧土家鲜肉饼-【低GI】2个黑椒牛肉饼',
    '支付方式', '美团月付(极速支付)', '入账情况', '已计入8月账单',
    '支付时间', '2026-07-24 18:51:36', '下单平台', '美团', '消费类型', '美食团购',
  ];

  it('美团：跳过 base64 哈希取商户（牛爷烧土家鲜肉饼）', () => {
    const event = parseAccessibilityTexts(MEITUAN_TEXTS, 'com.sankuai.meituan');
    expect(event).not.toBeNull();
    expect(event?.counterparty).toBe('牛爷烧土家鲜肉饼');
  });

  it('美团：金额 9.36，方向 expense（非 income）', () => {
    const event = parseAccessibilityTexts(MEITUAN_TEXTS, 'com.sankuai.meituan');
    expect(event?.amount).toBe('9.36');
    expect(event?.direction).toBe('expense');
  });

  it('美团：付款方式提取（美团月付极速支付）', () => {
    const event = parseAccessibilityTexts(MEITUAN_TEXTS, 'com.sankuai.meituan');
    expect(event?.raw['收/付款方式']).toBe('美团月付(极速支付)');
  });

  it('美团：时间提取（支付时间标签）', () => {
    const event = parseAccessibilityTexts(MEITUAN_TEXTS, 'com.sankuai.meituan');
    expect(event?.occurredAt).toBe('2026-07-24 18:51');
  });

  it('美团非消费页返回 null', () => {
    expect(parseAccessibilityTexts(['返回', '我的订单', '待使用'], 'com.sankuai.meituan')).toBeNull();
  });
});

describe('parseJingdongTexts（京东账单详情页）', () => {
  // 真机日志：金额带负号，商户在金额前
  const JD_TEXTS = [
    'dancing', 'dancing', '账单详情', '京东外卖', '-12.30', '交易成功',
    '付款方式', '白条', 'dancing', '付款形式', '先享后付自动扣款',
    '服务详情', '共1笔订单', '【新品推荐】江西炒粉+香辣猪耳+鸡蛋。', '0', '耳蛋炒',
    '创建时间 2026-07-2418:54:58', '账单分类', '食品酒饮',
  ];

  it('京东：带负号金额 -12.30，方向 expense', () => {
    const event = parseAccessibilityTexts(JD_TEXTS, 'com.jingdong.app.mall');
    expect(event).not.toBeNull();
    expect(event?.amount).toBe('12.30');
    expect(event?.direction).toBe('expense');
  });

  it('京东：商户取金额前节点（京东外卖）', () => {
    const event = parseAccessibilityTexts(JD_TEXTS, 'com.jingdong.app.mall');
    expect(event?.counterparty).toBe('京东外卖');
  });

  it('京东：付款方式提取（白条）', () => {
    const event = parseAccessibilityTexts(JD_TEXTS, 'com.jingdong.app.mall');
    expect(event?.raw['收/付款方式']).toBe('白条');
  });

  it('京东：时间提取（含日期时间粘连修复）', () => {
    const event = parseAccessibilityTexts(JD_TEXTS, 'com.jingdong.app.mall');
    expect(event?.occurredAt).toContain('2026-07-24');
    expect(event?.occurredAt).toContain('18:54');
  });

  it('京东非账单页返回 null', () => {
    expect(parseAccessibilityTexts(['返回', '我的京东', '待收货'], 'com.jingdong.app.mall')).toBeNull();
  });
});

describe('parseDouyinTexts（抖音月付还款页）', () => {
  // 真机日志：标题即商户，金额纯数字
  const DOUYIN_TEXTS = [
    '加防', '抖音月付还款', '5.88', '支付成功', '还款时间', '2026-06-1320:18:44',
    '支付方式', '支支付宝', '点击展开', '交易订单', '主动还款-抖音月付2026年6月账单',
  ];

  it('抖音：商户固定为"抖音月付还款"', () => {
    const event = parseAccessibilityTexts(DOUYIN_TEXTS, 'com.ss.android.ugc.aweme');
    expect(event).not.toBeNull();
    expect(event?.counterparty).toBe('抖音月付还款');
  });

  it('抖音：金额 5.88，方向 expense（还款是支出）', () => {
    const event = parseAccessibilityTexts(DOUYIN_TEXTS, 'com.ss.android.ugc.aweme');
    expect(event?.amount).toBe('5.88');
    expect(event?.direction).toBe('expense');
  });

  it('抖音：还款时间提取（日期时间粘连 2026-06-1320:18:44）', () => {
    const event = parseAccessibilityTexts(DOUYIN_TEXTS, 'com.ss.android.ugc.aweme');
    expect(event?.occurredAt).toBe('2026-06-13 20:18');
  });

  it('抖音：付款方式提取（支支付宝）', () => {
    const event = parseAccessibilityTexts(DOUYIN_TEXTS, 'com.ss.android.ugc.aweme');
    expect(event?.raw['收/付款方式']).toBe('支支付宝');
  });

  it('抖音非还款页返回 null', () => {
    expect(parseAccessibilityTexts(['返回', '首页', '推荐'], 'com.ss.android.ugc.aweme')).toBeNull();
  });
});
