import { describe, expect, it, beforeEach, vi } from 'vitest';

// mock expo-file-system，避免 react-native flow 语法在 vitest 下解析失败
// txKeyStore 的内存 Set（processedTxKeys）保持真实，仅文件持久化被 stub
vi.mock('expo-file-system/legacy', () => ({
  documentDirectory: '',
  getInfoAsync: vi.fn().mockResolvedValue({ exists: false }),
  readAsStringAsync: vi.fn().mockResolvedValue('[]'),
  writeAsStringAsync: vi.fn().mockResolvedValue(undefined),
  moveAsync: vi.fn().mockResolvedValue(undefined),
}));

import { useLedgerStore } from '../src/store/ledgerStore';
import { MemoryBackend } from '../src/domain/platform/mobileStore';
import { addProcessedTxKey, hasProcessedTxKey, clearProcessedTxKeys } from '../src/services/security/txKeyStore';
import type { LedgerFile } from '../src/domain/core/types';

const sampleLedger: LedgerFile = {
  path: 'main.bean',
  content: `option "operating_currency" "CNY"
2026-01-01 open Assets:Alipay CNY
2026-01-01 open Expenses:Food CNY
2026-01-01 open Expenses:Uncategorized CNY
2026-01-01 open Income:Uncategorized CNY
include "mobile.bean"
`,
};

const usdLedger: LedgerFile = {
  path: 'main.bean',
  content: `option "operating_currency" "USD"
2026-01-01 open Assets:Bank USD
2026-01-01 open Expenses:Food USD
include "mobile.bean"
`,
};

beforeEach(() => {
  useLedgerStore.getState().reset();
  clearProcessedTxKeys();
});

describe('ledgerStore 写操作', () => {
  it('autoOpenAccounts 跳过已 open 的账户（去重）', async () => {
    await useLedgerStore.getState().loadLedger([sampleLedger], new MemoryBackend());

    // Assets:Alipay 已存在 → 应跳过
    await useLedgerStore.getState().autoOpenAccounts(['Assets:Alipay', 'Assets:NewAccount']);
    const after = useLedgerStore.getState().mobileBean;

    // 只新增 Assets:NewAccount
    expect(after).toContain('Assets:NewAccount');
    // 不应添加重复的 open Assets:Alipay（只保留原有的）
    const openAlipayCount = (after.match(/open Assets:Alipay/g) ?? []).length;
    expect(openAlipayCount).toBe(1); // 只有原始的一条，没有重复添加
  });

  it('autoOpenAccounts 对已全部 open 的列表是 no-op', async () => {
    await useLedgerStore.getState().loadLedger([sampleLedger], new MemoryBackend());
    const before = useLedgerStore.getState().mobileBean;

    await useLedgerStore.getState().autoOpenAccounts(['Assets:Alipay']);
    const after = useLedgerStore.getState().mobileBean;

    expect(after).toBe(before); // 无变化
  });

  it('autoOpenAccounts 使用 ledger 的 operating_currency', async () => {
    await useLedgerStore.getState().loadLedger([usdLedger], new MemoryBackend());
    await useLedgerStore.getState().autoOpenAccounts(['Assets:NewUSD']);
    const after = useLedgerStore.getState().mobileBean;

    // 应写 USD 而非 CNY
    expect(after).toContain('open Assets:NewUSD USD');
    expect(after).not.toContain('open Assets:NewUSD CNY');
  });

  it('adjustAccountBalance 使用 ledger 的 operating_currency', async () => {
    await useLedgerStore.getState().loadLedger([usdLedger], new MemoryBackend());
    await useLedgerStore.getState().adjustAccountBalance('Assets:Bank', '5000', '2026-07-15');
    const after = useLedgerStore.getState().mobileBean;

    // balance 断言应使用 USD
    expect(after).toContain('balance Assets:Bank 5000 USD');
  });

  it('appendTransactionsBatch 在锁内拼接多笔交易', async () => {
    await useLedgerStore.getState().loadLedger([sampleLedger], new MemoryBackend());
    const before = useLedgerStore.getState().mobileBean;

    await useLedgerStore.getState().appendTransactionsBatch([
      { date: '2026-07-10', narration: '午餐', postings: [{ account: 'Assets:Alipay', amount: '-20', currency: 'CNY' }, { account: 'Expenses:Food', amount: '20', currency: 'CNY' }] },
      { date: '2026-07-11', narration: '晚餐', postings: [{ account: 'Assets:Alipay', amount: '-30', currency: 'CNY' }, { account: 'Expenses:Food', amount: '30', currency: 'CNY' }] },
    ]);

    const after = useLedgerStore.getState().mobileBean;
    expect(after).toContain('午餐');
    expect(after).toContain('晚餐');
    expect(after.length).toBeGreaterThan(before.length);
  });

  it('并发 addTransaction + autoOpenAccounts 不丢失数据', async () => {
    await useLedgerStore.getState().loadLedger([sampleLedger], new MemoryBackend());

    // 并发执行：添加交易 + 自动开户
    await Promise.all([
      useLedgerStore.getState().addTransaction({
        date: '2026-07-10', narration: '交易A',
        postings: [{ account: 'Assets:Alipay', amount: '-10', currency: 'CNY' }, { account: 'Expenses:Food', amount: '10', currency: 'CNY' }],
      }),
      useLedgerStore.getState().autoOpenAccounts(['Assets:NewAcct']),
    ]);

    const after = useLedgerStore.getState().mobileBean;
    // 两者都应存在（互斥锁保证了不丢失）
    expect(after).toContain('交易A');
    expect(after).toContain('Assets:NewAcct');
  });

  it('并发两笔 addTransaction 都保留', async () => {
    await useLedgerStore.getState().loadLedger([sampleLedger], new MemoryBackend());

    await Promise.all([
      useLedgerStore.getState().addTransaction({
        date: '2026-07-10', narration: '交易1',
        postings: [{ account: 'Assets:Alipay', amount: '-5', currency: 'CNY' }, { account: 'Expenses:Food', amount: '5', currency: 'CNY' }],
      }),
      useLedgerStore.getState().addTransaction({
        date: '2026-07-10', narration: '交易2',
        postings: [{ account: 'Assets:Alipay', amount: '-8', currency: 'CNY' }, { account: 'Expenses:Food', amount: '8', currency: 'CNY' }],
      }),
    ]);

    const txns = useLedgerStore.getState().ledger!.transactions;
    expect(txns).toHaveLength(2);
    expect(txns.map(t => t.narration).sort()).toEqual(['交易1', '交易2']);
  });
});

describe('deleteTransaction 清除去重键（删了重记流程）', () => {
  it('删除交易后清除对应的去重键，允许重新识别', async () => {
    await useLedgerStore.getState().loadLedger([sampleLedger], new MemoryBackend());

    // 模拟一笔已入账交易（盛香亭 66.8）
    await useLedgerStore.getState().addTransaction({
      date: '2026-07-24', narration: '支出', payee: '盛香亭新式热卤',
      postings: [{ account: 'Assets:Alipay', amount: '-66.8', currency: 'CNY' }, { account: 'Expenses:Food', amount: '66.8', currency: 'CNY' }],
    });
    const tx = useLedgerStore.getState().ledger!.transactions[0];

    // 模拟入账确认时写入去重键（与 automationPipeline 格式一致）
    const txKey = '2026-07-24_66.8_盛香亭新式热卤';
    addProcessedTxKey(txKey);
    expect(hasProcessedTxKey(txKey)).toBe(true); // 入账后已被去重拦截

    // 删除交易（模拟用户发现账户错了，删除重记）
    await useLedgerStore.getState().deleteTransaction(tx.raw);

    // 去重键应被清除，允许重新识别
    expect(hasProcessedTxKey(txKey)).toBe(false);
  });

  it('删除交易时 raw 格式异常也安全（不抛错，只是不清键）', async () => {
    await useLedgerStore.getState().loadLedger([sampleLedger], new MemoryBackend());
    // 写入一个不相关的键，确认不会被误删
    addProcessedTxKey('2026-07-24_99.99_某商户');

    // 添加并删除一笔正常交易
    await useLedgerStore.getState().addTransaction({
      date: '2026-07-24', narration: '测试',
      postings: [{ account: 'Assets:Alipay', amount: '-10', currency: 'CNY' }, { account: 'Expenses:Food', amount: '10', currency: 'CNY' }],
    });
    const tx = useLedgerStore.getState().ledger!.transactions[0];
    await useLedgerStore.getState().deleteTransaction(tx.raw);

    // 不相关的键不受影响
    expect(hasProcessedTxKey('2026-07-24_99.99_某商户')).toBe(true);
  });
});
