import { WalWatcher } from '../../../../../src/modules/netaclaw/runtime/weixin_db/wal_watcher.js';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';

describe('WalWatcher', () => {
  let tmpDir: string;
  let mainDb: string;
  let walPath: string;

  beforeEach(() => {
    tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'wal-watcher-'));
    mainDb = path.join(tmpDir, 'message_0.db');
    fs.writeFileSync(mainDb, 'db');
    walPath = mainDb + '-wal';
  });

  afterEach(() => {
    fs.rmSync(tmpDir, { recursive: true, force: true });
  });

  it('walPath appends -wal suffix', () => {
    const w = new WalWatcher(mainDb, async () => {}, 50);
    expect(w.walPath).toBe(walPath);
  });

  it('fires onChange when wal mtime advances', async () => {
    // 先放一个初始 wal 文件
    fs.writeFileSync(walPath, 'v1');
    let callCount = 0;
    const w = new WalWatcher(mainDb, async () => { callCount++; }, 20);
    w.start();
    // 等 1 tick 之后 mutate wal,再等 1-2 tick 看 callback 触发
    await new Promise(r => setTimeout(r, 60));
    expect(callCount).toBe(0); // 未变化,不触发
    const futureMtime = new Date(Date.now() + 5000);
    fs.utimesSync(walPath, futureMtime, futureMtime);
    await new Promise(r => setTimeout(r, 100));
    w.stop();
    expect(callCount).toBeGreaterThanOrEqual(1);
  });

  it('does not fire when wal does not exist', async () => {
    // 不创建 wal
    let callCount = 0;
    const w = new WalWatcher(mainDb, async () => { callCount++; }, 20);
    w.start();
    await new Promise(r => setTimeout(r, 80));
    w.stop();
    expect(callCount).toBe(0);
  });

  it('stop is idempotent', () => {
    const w = new WalWatcher(mainDb, async () => {}, 50);
    w.start();
    w.stop();
    expect(() => w.stop()).not.toThrow();
  });

  it('swallows errors in onChange without crashing', async () => {
    fs.writeFileSync(walPath, 'v1');
    const w = new WalWatcher(mainDb, async () => { throw new Error('boom'); }, 20);
    w.start();
    const futureMtime = new Date(Date.now() + 5000);
    fs.utimesSync(walPath, futureMtime, futureMtime);
    await new Promise(r => setTimeout(r, 80));
    w.stop();
    // 没 unhandled rejection/throw 就通过
  });
});
