import { beforeEach, describe, expect, it, mock } from 'bun:test';
import { createServiceCache, ServiceCache } from '../src/service-cache';
import {
  generateCachePrefix,
  getFullQueueName,
  normalizeServiceName,
  parseQueueName,
} from '../src/service-utils';
import type { BatchJobData } from '../src/types';

describe('Service Utilities', () => {
  describe('normalizeServiceName', () => {
    it('should normalize service names', () => {
      expect(normalizeServiceName('MyService')).toBe('my-service');
      expect(normalizeServiceName('webApi')).toBe('web-api');
      expect(normalizeServiceName('dataIngestion')).toBe('data-ingestion');
      expect(normalizeServiceName('data-pipeline')).toBe('data-pipeline');
      expect(normalizeServiceName('UPPERCASE')).toBe('uppercase');
    });

    it('should handle empty string', () => {
      expect(normalizeServiceName('')).toBe('');
    });

    it('should handle special characters', () => {
      // The function only handles camelCase, not special characters
      expect(normalizeServiceName('my@service#123')).toBe('my@service#123');
      expect(normalizeServiceName('serviceWithCamelCase')).toBe('service-with-camel-case');
    });
  });

  describe('generateCachePrefix', () => {
    it('should generate cache prefix', () => {
      expect(generateCachePrefix('service')).toBe('cache:service');
      expect(generateCachePrefix('webApi')).toBe('cache:web-api');
    });

    it('should handle empty parts', () => {
      expect(generateCachePrefix('')).toBe('cache:');
    });
  });

  describe('getFullQueueName', () => {
    it('should generate full queue name', () => {
      expect(getFullQueueName('service', 'handler')).toBe('{service_handler}');
      expect(getFullQueueName('webApi', 'handler')).toBe('{web-api_handler}');
    });

    it('should normalize service name', () => {
      expect(getFullQueueName('MyService', 'handler')).toBe('{my-service_handler}');
    });
  });

  describe('parseQueueName', () => {
    it('should parse queue name', () => {
      expect(parseQueueName('{service_handler}')).toEqual({
        service: 'service',
        handler: 'handler',
      });
      expect(parseQueueName('{web-api_data-processor}')).toEqual({
        service: 'web-api',
        handler: 'data-processor',
      });
    });

    it('should handle invalid formats', () => {
      expect(parseQueueName('service:handler')).toBeNull();
      expect(parseQueueName('service')).toBeNull();
      expect(parseQueueName('')).toBeNull();
    });

    it('should handle edge cases', () => {
      expect(parseQueueName('{}_handler')).toBeNull();
      expect(parseQueueName('{service_}')).toBeNull();
      expect(parseQueueName('not-a-valid-format')).toBeNull();
    });
  });
});

describe('ServiceCache', () => {
  it('should create service cache', () => {
    const mockRedisConfig = {
      host: 'localhost',
      port: 6379,
    };

    // Since ServiceCache constructor internally creates a real cache,
    // we can't easily test it without mocking the createCache function
    // For now, just test that the function exists and returns something
    const serviceCache = createServiceCache('myservice', mockRedisConfig);
    expect(serviceCache).toBeDefined();
    expect(serviceCache).toBeInstanceOf(ServiceCache);
  });

  it('should handle cache prefix correctly', () => {
    const mockRedisConfig = {
      host: 'localhost',
      port: 6379,
    };

    const serviceCache = createServiceCache('webApi', mockRedisConfig);
    expect(serviceCache).toBeDefined();
    // The prefix is set internally as cache:web-api
    expect(serviceCache.getKey('test')).toBe('cache:web-api:test');
  });

  it('should support global cache option', () => {
    const mockRedisConfig = {
      host: 'localhost',
      port: 6379,
    };

    const globalCache = createServiceCache('myservice', mockRedisConfig, { global: true });
    expect(globalCache).toBeDefined();
    // Global cache uses a different prefix
    expect(globalCache.getKey('test')).toBe('stock-bot:shared:test');
  });
});

describe('Batch Processing', () => {
  it('should handle batch job data types', () => {
    const batchJob: BatchJobData = {
      items: [1, 2, 3],
      options: {
        batchSize: 10,
        concurrency: 2,
      },
    };

    expect(batchJob.items).toHaveLength(3);
    expect(batchJob.options.batchSize).toBe(10);
    expect(batchJob.options.concurrency).toBe(2);
  });

  it('should process batch results', () => {
    const results = {
      totalItems: 10,
      successful: 8,
      failed: 2,
      errors: [
        { item: 5, error: 'Failed to process' },
        { item: 7, error: 'Invalid data' },
      ],
      duration: 1000,
    };

    expect(results.successful + results.failed).toBe(results.totalItems);
    expect(results.errors).toHaveLength(results.failed);
  });
});

describe('Rate Limiting', () => {
  it('should validate rate limit config', () => {
    const config = {
      rules: [
        {
          name: 'default',
          maxJobs: 100,
          window: 60000,
        },
        {
          name: 'api',
          maxJobs: 10,
          window: 1000,
        },
      ],
    };

    expect(config.rules).toHaveLength(2);
    expect(config.rules[0].name).toBe('default');
    expect(config.rules[1].maxJobs).toBe(10);
  });
});

describe('Queue Types', () => {
  it('should validate job data structure', () => {
    const jobData = {
      handler: 'TestHandler',
      operation: 'process',
      payload: { data: 'test' },
    };

    expect(jobData.handler).toBe('TestHandler');
    expect(jobData.operation).toBe('process');
    expect(jobData.payload).toBeDefined();
  });

  it('should validate queue stats structure', () => {
    const stats = {
      waiting: 10,
      active: 2,
      completed: 100,
      failed: 5,
      delayed: 3,
      paused: false,
      workers: 4,
    };

    expect(stats.waiting + stats.active + stats.completed + stats.failed + stats.delayed).toBe(120);
    expect(stats.paused).toBe(false);
    expect(stats.workers).toBe(4);
  });
});
