/**
 * Tests for Redis cache client
 */

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { CacheClient } from './redis';

// Mock redis client
vi.mock('redis', () => {
  const mockClient = {
    get: vi.fn(),
    setEx: vi.fn(),
    del: vi.fn(),
    exists: vi.fn(),
    keys: vi.fn(),
    on: vi.fn(),
    connect: vi.fn().mockResolvedValue(undefined),
    quit: vi.fn().mockResolvedValue(undefined),
  };

  return {
    createClient: vi.fn(() => mockClient),
  };
});

describe('CacheClient', () => {
  let cacheClient: CacheClient;
  let mockRedisClient: any;

  beforeEach(async () => {
    vi.clearAllMocks();
    const redis = await import('redis');
    mockRedisClient = (redis.createClient as any)();
    cacheClient = new CacheClient({ url: 'redis://localhost:6379' });
    await cacheClient.connect();
  });

  describe('get', () => {
    it('should get value from cache', async () => {
      mockRedisClient.get.mockResolvedValue('{"key": "value"}');
      
      const value = await cacheClient.get<{ key: string }>('test-key');
      
      expect(value).toEqual({ key: 'value' });
      expect(mockRedisClient.get).toHaveBeenCalledWith('the-order:test-key');
    });

    it('should return null if key not found', async () => {
      mockRedisClient.get.mockResolvedValue(null);
      
      const value = await cacheClient.get('nonexistent-key');
      
      expect(value).toBeNull();
    });

    it('should handle errors gracefully', async () => {
      mockRedisClient.get.mockRejectedValue(new Error('Redis error'));
      
      const value = await cacheClient.get('error-key');
      
      expect(value).toBeNull();
    });
  });

  describe('set', () => {
    it('should set value in cache', async () => {
      mockRedisClient.setEx.mockResolvedValue('OK');
      
      await cacheClient.set('test-key', { key: 'value' }, 3600);
      
      expect(mockRedisClient.setEx).toHaveBeenCalledWith(
        'the-order:test-key',
        3600,
        '{"key":"value"}'
      );
    });

    it('should use default TTL if not provided', async () => {
      mockRedisClient.setEx.mockResolvedValue('OK');
      const client = new CacheClient({ url: 'redis://localhost:6379', ttl: 7200 });
      await client.connect();
      
      await client.set('test-key', 'value');
      
      expect(mockRedisClient.setEx).toHaveBeenCalledWith(
        'the-order:test-key',
        7200,
        '"value"'
      );
    });
  });

  describe('delete', () => {
    it('should delete key from cache', async () => {
      mockRedisClient.del.mockResolvedValue(1);
      
      await cacheClient.delete('test-key');
      
      expect(mockRedisClient.del).toHaveBeenCalledWith('the-order:test-key');
    });
  });

  describe('invalidate', () => {
    it('should invalidate keys by pattern', async () => {
      mockRedisClient.keys.mockResolvedValue(['the-order:test-key1', 'the-order:test-key2']);
      mockRedisClient.del.mockResolvedValue(2);
      
      const deleted = await cacheClient.invalidate('test-key*');
      
      expect(deleted).toBe(2);
      expect(mockRedisClient.keys).toHaveBeenCalledWith('the-order:test-key*');
    });

    it('should return 0 if no keys found', async () => {
      mockRedisClient.keys.mockResolvedValue([]);
      
      const deleted = await cacheClient.invalidate('nonexistent*');
      
      expect(deleted).toBe(0);
    });
  });

  describe('exists', () => {
    it('should check if key exists', async () => {
      mockRedisClient.exists.mockResolvedValue(1);
      
      const exists = await cacheClient.exists('test-key');
      
      expect(exists).toBe(true);
      expect(mockRedisClient.exists).toHaveBeenCalledWith('the-order:test-key');
    });

    it('should return false if key does not exist', async () => {
      mockRedisClient.exists.mockResolvedValue(0);
      
      const exists = await cacheClient.exists('nonexistent-key');
      
      expect(exists).toBe(false);
    });
  });

  describe('getStats', () => {
    it('should return cache statistics', () => {
      const stats = cacheClient.getStats();
      
      expect(stats).toHaveProperty('hits');
      expect(stats).toHaveProperty('misses');
      expect(stats).toHaveProperty('sets');
      expect(stats).toHaveProperty('deletes');
      expect(stats).toHaveProperty('errors');
    });
  });

  describe('resetStats', () => {
    it('should reset cache statistics', async () => {
      mockRedisClient.get.mockResolvedValue('{"key": "value"}');
      await cacheClient.get('test-key');
      
      cacheClient.resetStats();
      const stats = cacheClient.getStats();
      
      expect(stats.hits).toBe(0);
      expect(stats.misses).toBe(0);
    });
  });
});

