import { WeixinDbService } from '../../../../src/modules/netaclaw/service/weixin_db.js';

jest.mock('../../../../src/modules/netaclaw/runtime/weixin_db/key_extractor.js', () => ({
  extractWeixinKeys: jest.fn(),
}));
jest.mock('../../../../src/modules/netaclaw/runtime/weixin_db/db_paths.js', () => ({
  DbPaths: class MockDbPaths {
    messageDb = 'm';
    sessionDb = 's';
    contactDb = 'c';
    static autoFindSeedDir() { return 'seed'; }
  },
}));
jest.mock('../../../../src/modules/netaclaw/runtime/weixin_db/incremental_reader.js', () => ({
  IncrementalReader: jest.fn().mockImplementation(() => ({
    initialize: jest.fn().mockResolvedValue(undefined),
    readIncrement: jest.fn().mockResolvedValue([]),
    resolveRoom: jest.fn(),
  })),
}));
jest.mock('../../../../src/modules/netaclaw/runtime/weixin_db/wal_watcher.js', () => ({
  WalWatcher: jest.fn().mockImplementation(() => ({ start: jest.fn(), stop: jest.fn() })),
}));
jest.mock('../../../../src/comm/data-dir.js', () => ({
  resolveDataDir: () => process.cwd(),
}));

describe('WeixinDbService', () => {
  let svc: WeixinDbService;

  beforeEach(() => {
    svc = new WeixinDbService();
    (svc as any).logger = { info: jest.fn(), warn: jest.fn(), error: jest.fn() };
    (svc as any).groupRepo = { find: jest.fn().mockResolvedValue([]) };
    (svc as any).channelRepo = { update: jest.fn().mockResolvedValue(undefined), findOne: jest.fn() };
  });

  it('bindChannel registers runtime when all keys found (win32)', async () => {
    if (process.platform !== 'win32') return; // 非 Windows 上此测试 skip
    const { extractWeixinKeys } = require('../../../../src/modules/netaclaw/runtime/weixin_db/key_extractor.js');
    extractWeixinKeys.mockResolvedValue({
      seedDir: 'seed', wxid: 'wxid_x', wechatVersion: '4.1', pid: 12345,
      dbKeys: {
        'message_0.db': '00'.repeat(32),
        'session.db': '11'.repeat(32),
        'contact.db': '22'.repeat(32),
      },
    });
    await svc.bindChannel({ id: 7, credential: null } as any, jest.fn());
    expect((svc as any).runtimes.has(7)).toBe(true);
  });

  it('bindChannel skips when keys missing (win32)', async () => {
    if (process.platform !== 'win32') return;
    const { extractWeixinKeys } = require('../../../../src/modules/netaclaw/runtime/weixin_db/key_extractor.js');
    extractWeixinKeys.mockResolvedValue({
      seedDir: 'seed', wxid: 'wxid', wechatVersion: '4.1', pid: 1, dbKeys: {}
    });
    await svc.bindChannel({ id: 7, credential: null } as any, jest.fn());
    expect((svc as any).runtimes.has(7)).toBe(false);
  });

  it('unbindChannel is idempotent', () => {
    expect(() => svc.unbindChannel(99)).not.toThrow();
  });

  it('refreshWhitelist populates cache from enabled groups', async () => {
    (svc as any).groupRepo = {
      find: jest.fn().mockResolvedValue([
        { roomName: '产品研发群' }, { roomName: '家庭群' }, { roomName: null },
      ]),
    };
    await svc.refreshWhitelist(7);
    const snapshot = (svc as any).whitelistCache.get(7);
    expect(snapshot).toEqual(new Set(['产品研发群', '家庭群']));
  });
});
