import { IoAdapter } from '@nestjs/platform-socket.io';
import { createAdapter } from '@socket.io/redis-adapter';
import { RedisService } from '../redis/redis.service';
import { RedisIoAdapter } from './redis-io.adapter';

jest.mock('@socket.io/redis-adapter', () => ({ createAdapter: jest.fn(() => 'redis-adapter') }));

describe('RedisIoAdapter', () => {
  const redis = { createPubSubClients: jest.fn() } as unknown as RedisService;
  let createServer: jest.SpyInstance;

  beforeEach(() => {
    jest.clearAllMocks();
    createServer = jest.spyOn(IoAdapter.prototype, 'createIOServer').mockReturnValue({ adapter: jest.fn() });
  });

  afterEach(() => createServer.mockRestore());

  it('uses the default Socket.IO adapter without Redis clients', async () => {
    (redis.createPubSubClients as jest.Mock).mockReturnValue(null);
    const adapter = new RedisIoAdapter({} as never, redis);
    await adapter.connectToRedis();
    const server = adapter.createIOServer(4000, { path: '/socket' } as never);
    expect(createAdapter).not.toHaveBeenCalled();
    expect(server.adapter).not.toHaveBeenCalled();
  });

  it('attaches Redis adapter and closes both clients safely', async () => {
    const pubClient = { quit: jest.fn().mockResolvedValue('OK'), disconnect: jest.fn() };
    const subClient = { quit: jest.fn().mockRejectedValue(new Error('closed')), disconnect: jest.fn() };
    (redis.createPubSubClients as jest.Mock).mockReturnValue({ pubClient, subClient });
    const adapter = new RedisIoAdapter({} as never, redis);
    await adapter.connectToRedis();
    const server = adapter.createIOServer(4000);
    expect(createAdapter).toHaveBeenCalledWith(pubClient, subClient);
    expect(server.adapter).toHaveBeenCalledWith('redis-adapter');
    await adapter.close();
    expect(pubClient.quit).toHaveBeenCalled();
    expect(subClient.disconnect).toHaveBeenCalled();
    await adapter.close();
    expect(pubClient.quit).toHaveBeenCalledTimes(1);
  });
});
