import { IncrementalSMA, TechnicalIndicators } from '@stock-bot/engine';
import { IncrementalIndicators, SignalGenerator, TechnicalAnalysis } from '../src/indicators/TechnicalAnalysis';

describe('Technical Analysis Library', () => {
  let ta: TechnicalAnalysis;
  let indicators: TechnicalIndicators;

  beforeEach(() => {
    ta = new TechnicalAnalysis();
    indicators = new TechnicalIndicators();
  });

  describe('Simple Moving Average', () => {
    it('should calculate SMA correctly', () => {
      const values = [10, 12, 13, 14, 15, 16, 17, 18, 19, 20];
      const sma = ta.sma(values, 5);
      
      expect(sma).toHaveLength(6); // 10 values - 5 period + 1
      expect(sma[0]).toBeCloseTo(12.8); // (10+12+13+14+15)/5
      expect(sma[5]).toBeCloseTo(18); // (16+17+18+19+20)/5
    });

    it('should handle incremental SMA updates', () => {
      const incSMA = new IncrementalSMA(3);
      
      expect(incSMA.update(10)).toBeNull();
      expect(incSMA.update(12)).toBeNull();
      expect(incSMA.update(14)).toBeCloseTo(12); // (10+12+14)/3
      expect(incSMA.update(16)).toBeCloseTo(14); // (12+14+16)/3
      expect(incSMA.current()).toBeCloseTo(14);
    });
  });

  describe('Exponential Moving Average', () => {
    it('should calculate EMA correctly', () => {
      const values = [10, 12, 13, 14, 15, 16, 17, 18, 19, 20];
      const ema = ta.ema(values, 5);
      
      expect(ema).toHaveLength(6);
      expect(ema[0]).toBeGreaterThan(0);
      expect(ema[ema.length - 1]).toBeGreaterThan(ema[0]);
    });
  });

  describe('RSI', () => {
    it('should calculate RSI correctly', () => {
      const values = [
        44.34, 44.09, 44.15, 43.61, 44.33, 44.83, 45.10, 45.42,
        45.84, 46.08, 45.89, 46.03, 45.61, 46.28, 46.28, 46.00,
        46.03, 46.41, 46.22, 45.64
      ];
      const rsi = ta.rsi(values, 14);
      
      expect(rsi).toHaveLength(7); // 20 values - 14 period + 1
      expect(rsi[rsi.length - 1]).toBeGreaterThan(0);
      expect(rsi[rsi.length - 1]).toBeLessThan(100);
    });

    it('should identify overbought/oversold conditions', () => {
      // Trending up values should give high RSI
      const uptrend = Array.from({ length: 20 }, (_, i) => 100 + i);
      const rsiUp = ta.rsi(uptrend, 14);
      expect(rsiUp[rsiUp.length - 1]).toBeGreaterThan(70);
      
      // Trending down values should give low RSI
      const downtrend = Array.from({ length: 20 }, (_, i) => 100 - i);
      const rsiDown = ta.rsi(downtrend, 14);
      expect(rsiDown[rsiDown.length - 1]).toBeLessThan(30);
    });
  });

  describe('MACD', () => {
    it('should calculate MACD components correctly', () => {
      const values = Array.from({ length: 50 }, (_, i) => 100 + Math.sin(i * 0.1) * 10);
      const macd = ta.macd(values);
      
      expect(macd.macd).toHaveLength(39); // 50 - 26 + 1 - 9 + 1
      expect(macd.signal).toHaveLength(39);
      expect(macd.histogram).toHaveLength(39);
      
      // Histogram should be the difference between MACD and signal
      expect(macd.histogram[0]).toBeCloseTo(macd.macd[0] - macd.signal[0]);
    });
  });

  describe('Bollinger Bands', () => {
    it('should calculate bands correctly', () => {
      const values = Array.from({ length: 30 }, (_, i) => 100 + Math.random() * 10);
      const bb = ta.bollingerBands(values, 20, 2);
      
      expect(bb.middle).toHaveLength(11); // 30 - 20 + 1
      expect(bb.upper).toHaveLength(11);
      expect(bb.lower).toHaveLength(11);
      
      // Upper should be above middle, lower should be below
      for (let i = 0; i < bb.middle.length; i++) {
        expect(bb.upper[i]).toBeGreaterThan(bb.middle[i]);
        expect(bb.lower[i]).toBeLessThan(bb.middle[i]);
      }
    });
  });

  describe('ATR', () => {
    it('should calculate ATR correctly', () => {
      const high = [48.70, 48.72, 48.90, 48.87, 48.82, 49.05, 49.20, 49.35, 49.92, 50.19];
      const low = [47.79, 48.14, 48.39, 48.37, 48.24, 48.64, 48.94, 48.86, 49.50, 49.87];
      const close = [48.16, 48.61, 48.75, 48.63, 48.74, 49.03, 49.07, 49.32, 49.91, 50.13];
      
      const atr = ta.atr(high, low, close, 5);
      
      expect(atr).toHaveLength(5); // 10 - 5 - 1 + 1
      expect(atr.every(v => v > 0)).toBe(true);
    });
  });

  describe('Stochastic', () => {
    it('should calculate Stochastic correctly', () => {
      const high = Array.from({ length: 20 }, () => Math.random() * 10 + 100);
      const low = Array.from({ length: 20 }, (_, i) => high[i] - Math.random() * 5);
      const close = Array.from({ length: 20 }, (_, i) => (high[i] + low[i]) / 2);
      
      const stoch = ta.stochastic(high, low, close, 14, 3, 3);
      
      expect(stoch.k.length).toBeGreaterThan(0);
      expect(stoch.d.length).toBeGreaterThan(0);
      
      // %K and %D should be between 0 and 100
      expect(stoch.k.every(v => v >= 0 && v <= 100)).toBe(true);
      expect(stoch.d.every(v => v >= 0 && v <= 100)).toBe(true);
    });
  });

  describe('Signal Generator', () => {
    it('should generate trading signals based on indicators', () => {
      const generator = new SignalGenerator();
      
      // Create synthetic price data
      const prices = {
        close: Array.from({ length: 50 }, (_, i) => 100 + Math.sin(i * 0.2) * 10),
        high: Array.from({ length: 50 }, (_, i) => 102 + Math.sin(i * 0.2) * 10),
        low: Array.from({ length: 50 }, (_, i) => 98 + Math.sin(i * 0.2) * 10),
        volume: Array.from({ length: 50 }, () => 1000000)
      };
      
      const signal = generator.generateSignals('TEST', prices, Date.now());
      
      expect(signal.symbol).toBe('TEST');
      expect(['BUY', 'SELL', 'HOLD']).toContain(signal.action);
      expect(signal.strength).toBeGreaterThanOrEqual(0);
      expect(signal.strength).toBeLessThanOrEqual(1);
      expect(signal.indicators).toBeDefined();
      expect(signal.reason).toBeDefined();
    });
  });

  describe('Incremental Indicators Manager', () => {
    it('should manage multiple incremental indicators', () => {
      const manager = new IncrementalIndicators();
      
      manager.createSMA('fast', 10);
      manager.createSMA('slow', 20);
      manager.createRSI('rsi', 14);
      
      // Update all indicators with same value
      for (let i = 0; i < 25; i++) {
        const value = 100 + i;
        manager.update('fast', value);
        manager.update('slow', value);
        manager.update('rsi', value);
      }
      
      expect(manager.current('fast')).toBeDefined();
      expect(manager.current('slow')).toBeDefined();
      expect(manager.current('rsi')).toBeDefined();
      
      // RSI should be high for uptrending values
      const rsiValue = manager.current('rsi');
      expect(rsiValue).toBeGreaterThan(70);
    });
  });

  describe('Crossover Detection', () => {
    it('should detect crossovers correctly', () => {
      const series1 = [10, 11, 12, 13, 14];
      const series2 = [12, 12, 12, 12, 12];
      
      expect(TechnicalAnalysis.crossover(series1, series2)).toBe(true);
      expect(TechnicalAnalysis.crossunder(series2, series1)).toBe(true);
      
      const series3 = [15, 14, 13, 12, 11];
      expect(TechnicalAnalysis.crossunder(series3, series2)).toBe(true);
      expect(TechnicalAnalysis.crossover(series2, series3)).toBe(true);
    });
  });
});