import { Test, TestingModule } from '@nestjs/testing';
import { RedisService } from './redis.service';
import { REDIS_CLIENT } from './redis.constants';

describe('RedisService', () => {
  let service: RedisService;

  const mockClient = {
    get: jest.fn(),
    set: jest.fn(),
    del: jest.fn(),
    keys: jest.fn(),
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        RedisService,
        { provide: REDIS_CLIENT, useValue: mockClient },
      ],
    }).compile();

    service = module.get<RedisService>(RedisService);
  });

  afterEach(() => jest.clearAllMocks());

  describe('get', () => {
    it('should return value for existing key', async () => {
      mockClient.get.mockResolvedValue('some-value');

      const result = await service.get('my-key');

      expect(result).toBe('some-value');
      expect(mockClient.get).toHaveBeenCalledWith('my-key');
    });

    it('should return null for non-existing key', async () => {
      mockClient.get.mockResolvedValue(null);

      const result = await service.get('missing-key');

      expect(result).toBeNull();
    });
  });

  describe('set', () => {
    it('should set value without TTL when ttlSeconds not provided', async () => {
      mockClient.set.mockResolvedValue('OK');

      await service.set('key', 'value');

      expect(mockClient.set).toHaveBeenCalledWith('key', 'value');
    });

    it('should set value with TTL when ttlSeconds provided', async () => {
      mockClient.set.mockResolvedValue('OK');

      await service.set('key', 'value', 60);

      expect(mockClient.set).toHaveBeenCalledWith('key', 'value', 'EX', 60);
    });
  });

  describe('del', () => {
    it('should delete a key', async () => {
      mockClient.del.mockResolvedValue(1);

      await service.del('key');

      expect(mockClient.del).toHaveBeenCalledWith('key');
    });
  });

  describe('invalidateByPattern', () => {
    it('should delete all keys matching the pattern', async () => {
      const keys = ['perm:role:t1:r1', 'perm:role:t1:r2'];
      mockClient.keys.mockResolvedValue(keys);
      mockClient.del.mockResolvedValue(2);

      await service.invalidateByPattern('perm:role:t1:*');

      expect(mockClient.keys).toHaveBeenCalledWith('perm:role:t1:*');
      expect(mockClient.del).toHaveBeenCalledWith(...keys);
    });

    it('should not call del when no keys match', async () => {
      mockClient.keys.mockResolvedValue([]);

      await service.invalidateByPattern('perm:role:t1:*');

      expect(mockClient.keys).toHaveBeenCalledWith('perm:role:t1:*');
      expect(mockClient.del).not.toHaveBeenCalled();
    });
  });
});
