const { initRedis, closeRedis } = require('../config/redis');
const CacheUtil = require('../utils/cache');
const CacheService = require('../services/cacheService');

describe('Redis Cache Layer', () => {
  beforeAll(async () => {
    await initRedis();
  });

  afterAll(async () => {
    await closeRedis();
  });

  describe('CacheUtil', () => {
    const testKey = 'test:key';
    const testValue = { name: 'Test', value: 123 };

    afterEach(async () => {
      await CacheUtil.delete(testKey);
    });

    test('should set and get a value', async () => {
      await CacheUtil.set(testKey, testValue);
      const result = await CacheUtil.get(testKey);
      expect(result).toEqual(testValue);
    });

    test('should set a value with TTL', async () => {
      await CacheUtil.set(testKey, testValue, 10);
      const ttl = await CacheUtil.ttl(testKey);
      expect(ttl).toBeGreaterThan(0);
      expect(ttl).toBeLessThanOrEqual(10);
    });

    test('should delete a value', async () => {
      await CacheUtil.set(testKey, testValue);
      await CacheUtil.delete(testKey);
      const result = await CacheUtil.get(testKey);
      expect(result).toBeNull();
    });

    test('should set expiration on existing key', async () => {
      await CacheUtil.set(testKey, testValue);
      await CacheUtil.expire(testKey, 5);
      const ttl = await CacheUtil.ttl(testKey);
      expect(ttl).toBeGreaterThan(0);
      expect(ttl).toBeLessThanOrEqual(5);
    });

    test('should check if key exists', async () => {
      await CacheUtil.set(testKey, testValue);
      const exists = await CacheUtil.exists(testKey);
      expect(exists).toBe(true);

      await CacheUtil.delete(testKey);
      const notExists = await CacheUtil.exists(testKey);
      expect(notExists).toBe(false);
    });

    test('should delete keys by pattern', async () => {
      await CacheUtil.set('test:key1', { value: 1 });
      await CacheUtil.set('test:key2', { value: 2 });
      await CacheUtil.set('test:key3', { value: 3 });

      const deleted = await CacheUtil.deletePattern('test:key*');
      expect(deleted).toBe(3);
    });

    test('should return null for non-existent key', async () => {
      const result = await CacheUtil.get('non:existent:key');
      expect(result).toBeNull();
    });
  });

  describe('CacheService - Service Data Caching', () => {
    const mockServiceList = {
      services: [
        { id: '1', name: 'Service 1' },
        { id: '2', name: 'Service 2' },
      ],
      total: 2,
    };

    const mockServiceDetail = {
      id: '123',
      name: 'Test Service',
      description: 'Test Description',
    };

    const mockCategories = [
      { id: '1', name: 'Category 1' },
      { id: '2', name: 'Category 2' },
    ];

    afterEach(async () => {
      await CacheService.invalidateServiceCache();
      await CacheService.invalidateCategoryCache();
    });

    test('should cache and retrieve service list', async () => {
      const params = { page: 1, limit: 10 };
      await CacheService.setServiceList(params, mockServiceList);
      const result = await CacheService.getServiceList(params);
      expect(result).toEqual(mockServiceList);
    });

    test('should cache and retrieve service detail', async () => {
      const serviceId = '123';
      await CacheService.setServiceDetail(serviceId, mockServiceDetail);
      const result = await CacheService.getServiceDetail(serviceId);
      expect(result).toEqual(mockServiceDetail);
    });

    test('should cache and retrieve category list', async () => {
      await CacheService.setCategoryList(mockCategories);
      const result = await CacheService.getCategoryList();
      expect(result).toEqual(mockCategories);
    });

    test('should cache services by category', async () => {
      const categoryId = 'cat-1';
      const params = { page: 1, limit: 10 };
      await CacheService.setServicesByCategory(
        categoryId,
        params,
        mockServiceList
      );
      const result = await CacheService.getServicesByCategory(
        categoryId,
        params
      );
      expect(result).toEqual(mockServiceList);
    });

    test('should invalidate all service caches', async () => {
      await CacheService.setServiceList({ page: 1 }, mockServiceList);
      await CacheService.setServiceDetail('123', mockServiceDetail);

      const deleted = await CacheService.invalidateServiceCache();
      expect(deleted).toBeGreaterThan(0);

      const listResult = await CacheService.getServiceList({ page: 1 });
      const detailResult = await CacheService.getServiceDetail('123');
      expect(listResult).toBeNull();
      expect(detailResult).toBeNull();
    });

    test('should invalidate specific service detail', async () => {
      const serviceId = '123';
      await CacheService.setServiceDetail(serviceId, mockServiceDetail);
      await CacheService.invalidateServiceDetail(serviceId);
      const result = await CacheService.getServiceDetail(serviceId);
      expect(result).toBeNull();
    });

    test('should return null for non-cached service list', async () => {
      const result = await CacheService.getServiceList({ page: 99 });
      expect(result).toBeNull();
    });
  });
});
