import { describe, expect, it } from 'bun:test';
import { CacheKeyGenerator, generateKey } from '../src/key-generator';

describe('CacheKeyGenerator', () => {
  describe('marketData', () => {
    it('should generate key with symbol, timeframe and date', () => {
      const date = new Date('2024-01-15T10:30:00Z');
      const key = CacheKeyGenerator.marketData('AAPL', '1h', date);
      expect(key).toBe('market:aapl:1h:2024-01-15');
    });

    it('should generate key with "latest" when no date provided', () => {
      const key = CacheKeyGenerator.marketData('MSFT', '1d');
      expect(key).toBe('market:msft:1d:latest');
    });

    it('should lowercase the symbol', () => {
      const key = CacheKeyGenerator.marketData('GOOGL', '5m');
      expect(key).toBe('market:googl:5m:latest');
    });

    it('should handle different timeframes', () => {
      expect(CacheKeyGenerator.marketData('TSLA', '1m')).toBe('market:tsla:1m:latest');
      expect(CacheKeyGenerator.marketData('TSLA', '15m')).toBe('market:tsla:15m:latest');
      expect(CacheKeyGenerator.marketData('TSLA', '1w')).toBe('market:tsla:1w:latest');
    });
  });

  describe('indicator', () => {
    it('should generate key with all parameters', () => {
      const key = CacheKeyGenerator.indicator('AAPL', 'RSI', 14, 'abc123');
      expect(key).toBe('indicator:aapl:RSI:14:abc123');
    });

    it('should lowercase the symbol but not indicator name', () => {
      const key = CacheKeyGenerator.indicator('META', 'MACD', 20, 'hash456');
      expect(key).toBe('indicator:meta:MACD:20:hash456');
    });

    it('should handle different period values', () => {
      expect(CacheKeyGenerator.indicator('AMZN', 'SMA', 50, 'hash1')).toBe(
        'indicator:amzn:SMA:50:hash1'
      );
      expect(CacheKeyGenerator.indicator('AMZN', 'SMA', 200, 'hash2')).toBe(
        'indicator:amzn:SMA:200:hash2'
      );
    });
  });

  describe('backtest', () => {
    it('should generate key with strategy name and hashed params', () => {
      const params = { stopLoss: 0.02, takeProfit: 0.05 };
      const key = CacheKeyGenerator.backtest('MomentumStrategy', params);
      expect(key).toMatch(/^backtest:MomentumStrategy:[a-z0-9]+$/);
    });

    it('should generate same hash for same params regardless of order', () => {
      const params1 = { a: 1, b: 2, c: 3 };
      const params2 = { c: 3, a: 1, b: 2 };
      const key1 = CacheKeyGenerator.backtest('Strategy', params1);
      const key2 = CacheKeyGenerator.backtest('Strategy', params2);
      expect(key1).toBe(key2);
    });

    it('should generate different hashes for different params', () => {
      const params1 = { threshold: 0.01 };
      const params2 = { threshold: 0.02 };
      const key1 = CacheKeyGenerator.backtest('Strategy', params1);
      const key2 = CacheKeyGenerator.backtest('Strategy', params2);
      expect(key1).not.toBe(key2);
    });

    it('should handle complex nested params', () => {
      const params = {
        indicators: { rsi: { period: 14 }, macd: { fast: 12, slow: 26 } },
        risk: { maxDrawdown: 0.1 },
      };
      const key = CacheKeyGenerator.backtest('ComplexStrategy', params);
      expect(key).toMatch(/^backtest:ComplexStrategy:[a-z0-9]+$/);
    });
  });

  describe('strategy', () => {
    it('should generate key with strategy name, symbol and timeframe', () => {
      const key = CacheKeyGenerator.strategy('TrendFollowing', 'NVDA', '4h');
      expect(key).toBe('strategy:TrendFollowing:nvda:4h');
    });

    it('should lowercase the symbol but not strategy name', () => {
      const key = CacheKeyGenerator.strategy('MeanReversion', 'AMD', '1d');
      expect(key).toBe('strategy:MeanReversion:amd:1d');
    });
  });

  describe('userSession', () => {
    it('should generate key with userId', () => {
      const key = CacheKeyGenerator.userSession('user123');
      expect(key).toBe('session:user123');
    });

    it('should handle different userId formats', () => {
      expect(CacheKeyGenerator.userSession('uuid-123-456')).toBe('session:uuid-123-456');
      expect(CacheKeyGenerator.userSession('email@example.com')).toBe('session:email@example.com');
    });
  });

  describe('portfolio', () => {
    it('should generate key with userId and portfolioId', () => {
      const key = CacheKeyGenerator.portfolio('user123', 'portfolio456');
      expect(key).toBe('portfolio:user123:portfolio456');
    });

    it('should handle UUID format IDs', () => {
      const key = CacheKeyGenerator.portfolio(
        '550e8400-e29b-41d4-a716-446655440000',
        '6ba7b810-9dad-11d1-80b4-00c04fd430c8'
      );
      expect(key).toBe(
        'portfolio:550e8400-e29b-41d4-a716-446655440000:6ba7b810-9dad-11d1-80b4-00c04fd430c8'
      );
    });
  });

  describe('realtimePrice', () => {
    it('should generate key with symbol', () => {
      const key = CacheKeyGenerator.realtimePrice('BTC');
      expect(key).toBe('price:realtime:btc');
    });

    it('should lowercase the symbol', () => {
      const key = CacheKeyGenerator.realtimePrice('ETH-USD');
      expect(key).toBe('price:realtime:eth-usd');
    });
  });

  describe('orderBook', () => {
    it('should generate key with symbol and default depth', () => {
      const key = CacheKeyGenerator.orderBook('BTC');
      expect(key).toBe('orderbook:btc:10');
    });

    it('should generate key with custom depth', () => {
      const key = CacheKeyGenerator.orderBook('ETH', 20);
      expect(key).toBe('orderbook:eth:20');
    });

    it('should lowercase the symbol', () => {
      const key = CacheKeyGenerator.orderBook('USDT', 5);
      expect(key).toBe('orderbook:usdt:5');
    });
  });

  describe('hashObject', () => {
    it('should generate consistent hashes', () => {
      const params = { x: 1, y: 2 };
      const key1 = CacheKeyGenerator.backtest('Test', params);
      const key2 = CacheKeyGenerator.backtest('Test', params);
      expect(key1).toBe(key2);
    });

    it('should handle empty objects', () => {
      const key = CacheKeyGenerator.backtest('Empty', {});
      expect(key).toMatch(/^backtest:Empty:[a-z0-9]+$/);
    });

    it('should handle arrays in objects', () => {
      const params = { symbols: ['AAPL', 'MSFT'], periods: [10, 20, 30] };
      const key = CacheKeyGenerator.backtest('ArrayTest', params);
      expect(key).toMatch(/^backtest:ArrayTest:[a-z0-9]+$/);
    });

    it('should handle null and undefined values', () => {
      const params = { a: null, b: undefined, c: 'value' };
      const key = CacheKeyGenerator.backtest('NullTest', params);
      expect(key).toMatch(/^backtest:NullTest:[a-z0-9]+$/);
    });
  });
});

describe('generateKey', () => {
  it('should join parts with colons', () => {
    const key = generateKey('user', 123, 'data');
    expect(key).toBe('user:123:data');
  });

  it('should filter undefined values', () => {
    const key = generateKey('prefix', undefined, 'suffix');
    expect(key).toBe('prefix:suffix');
  });

  it('should convert all types to strings', () => {
    const key = generateKey('bool', true, 'num', 42, 'str', 'text');
    expect(key).toBe('bool:true:num:42:str:text');
  });

  it('should handle empty input', () => {
    const key = generateKey();
    expect(key).toBe('');
  });

  it('should handle single part', () => {
    const key = generateKey('single');
    expect(key).toBe('single');
  });

  it('should handle all undefined values', () => {
    const key = generateKey(undefined, undefined, undefined);
    expect(key).toBe('');
  });

  it('should handle boolean false', () => {
    const key = generateKey('flag', false, 'end');
    expect(key).toBe('flag:false:end');
  });

  it('should handle zero', () => {
    const key = generateKey('count', 0, 'items');
    expect(key).toBe('count:0:items');
  });
});
