import { BollingerBandsResult, IncrementalEMA, IncrementalRSI, IncrementalSMA, MacdResult, StochasticResult, TechnicalIndicators } from '@stock-bot/engine';

/**
 * Wrapper class for the Rust TA library with TypeScript-friendly interfaces
 */
export class TechnicalAnalysis {
  private indicators: TechnicalIndicators;

  constructor() {
    this.indicators = new TechnicalIndicators();
  }

  // Simple indicators
  sma(values: number[], period: number): number[] {
    return this.indicators.calculateSma(values, period);
  }

  ema(values: number[], period: number): number[] {
    return this.indicators.calculateEma(values, period);
  }

  rsi(values: number[], period: number): number[] {
    return this.indicators.calculateRsi(values, period);
  }

  atr(high: number[], low: number[], close: number[], period: number): number[] {
    return this.indicators.calculateAtr(high, low, close, period);
  }

  // Complex indicators with parsed results
  macd(values: number[], fastPeriod = 12, slowPeriod = 26, signalPeriod = 9): MacdResult {
    const result = this.indicators.calculateMacd(values, fastPeriod, slowPeriod, signalPeriod);
    return JSON.parse(result);
  }

  bollingerBands(values: number[], period = 20, stdDev = 2): BollingerBandsResult {
    const result = this.indicators.calculateBollingerBands(values, period, stdDev);
    return JSON.parse(result);
  }

  stochastic(
    high: number[], 
    low: number[], 
    close: number[], 
    kPeriod = 14, 
    dPeriod = 3, 
    smoothK = 1
  ): StochasticResult {
    const result = this.indicators.calculateStochastic(high, low, close, kPeriod, dPeriod, smoothK);
    return JSON.parse(result);
  }

  // Helper to get the latest value from an indicator array
  static latest(values: number[]): number | undefined {
    return values[values.length - 1];
  }

  // Helper to check for crossovers
  static crossover(series1: number[], series2: number[]): boolean {
    if (series1.length < 2 || series2.length < 2) {return false;}
    const prev1 = series1[series1.length - 2];
    const curr1 = series1[series1.length - 1];
    const prev2 = series2[series2.length - 2];
    const curr2 = series2[series2.length - 1];
    return prev1 <= prev2 && curr1 > curr2;
  }

  static crossunder(series1: number[], series2: number[]): boolean {
    if (series1.length < 2 || series2.length < 2) {return false;}
    const prev1 = series1[series1.length - 2];
    const curr1 = series1[series1.length - 1];
    const prev2 = series2[series2.length - 2];
    const curr2 = series2[series2.length - 1];
    return prev1 >= prev2 && curr1 < curr2;
  }
}

/**
 * Incremental indicator manager for streaming data
 */
export class IncrementalIndicators {
  private indicators: Map<string, IncrementalSMA | IncrementalEMA | IncrementalRSI> = new Map();

  createSMA(key: string, period: number): IncrementalSMA {
    const indicator = new IncrementalSMA(period);
    this.indicators.set(key, indicator);
    return indicator;
  }

  createEMA(key: string, period: number): IncrementalEMA {
    const indicator = new IncrementalEMA(period);
    this.indicators.set(key, indicator);
    return indicator;
  }

  createRSI(key: string, period: number): IncrementalRSI {
    const indicator = new IncrementalRSI(period);
    this.indicators.set(key, indicator);
    return indicator;
  }

  get(key: string): IncrementalSMA | IncrementalEMA | IncrementalRSI | undefined {
    return this.indicators.get(key);
  }

  update(key: string, value: number): number | null {
    const indicator = this.indicators.get(key);
    if (!indicator) {
      throw new Error(`Indicator ${key} not found`);
    }
    return indicator.update(value);
  }

  current(key: string): number | null {
    const indicator = this.indicators.get(key);
    if (!indicator) {
      throw new Error(`Indicator ${key} not found`);
    }
    return indicator.current();
  }

  reset(key: string): void {
    const indicator = this.indicators.get(key);
    if (indicator && 'reset' in indicator) {
      indicator.reset();
    }
  }

  resetAll(): void {
    this.indicators.forEach(indicator => {
      if ('reset' in indicator) {
        indicator.reset();
      }
    });
  }
}

/**
 * Signal generator using technical indicators
 */
export interface TradingSignal {
  symbol: string;
  timestamp: number;
  action: 'BUY' | 'SELL' | 'HOLD';
  strength: number; // 0-1
  indicators: Record<string, number>;
  reason: string;
}

export class SignalGenerator {
  private ta: TechnicalAnalysis;

  constructor() {
    this.ta = new TechnicalAnalysis();
  }

  /**
   * Generate signals based on multiple indicators
   */
  generateSignals(
    symbol: string,
    prices: {
      close: number[];
      high: number[];
      low: number[];
      volume: number[];
    },
    timestamp: number
  ): TradingSignal {
    const indicators: Record<string, number> = {};
    let buySignals = 0;
    let sellSignals = 0;
    let totalWeight = 0;
    const reasons: string[] = [];

    // RSI signals
    if (prices.close.length >= 14) {
      const rsi = this.ta.rsi(prices.close, 14);
      const currentRsi = TechnicalAnalysis.latest(rsi);
      if (currentRsi !== undefined) {
        indicators.rsi = currentRsi;
        if (currentRsi < 30) {
          buySignals += 2;
          totalWeight += 2;
          reasons.push('RSI oversold');
        } else if (currentRsi > 70) {
          sellSignals += 2;
          totalWeight += 2;
          reasons.push('RSI overbought');
        } else {
          totalWeight += 1;
        }
      }
    }

    // MACD signals
    if (prices.close.length >= 26) {
      const macd = this.ta.macd(prices.close);
      const currentMacd = TechnicalAnalysis.latest(macd.macd);
      const currentSignal = TechnicalAnalysis.latest(macd.signal);
      const currentHistogram = TechnicalAnalysis.latest(macd.histogram);
      
      if (currentMacd !== undefined && currentSignal !== undefined) {
        indicators.macd = currentMacd;
        indicators.macdSignal = currentSignal;
        indicators.macdHistogram = currentHistogram || 0;
        
        if (TechnicalAnalysis.crossover(macd.macd, macd.signal)) {
          buySignals += 3;
          totalWeight += 3;
          reasons.push('MACD bullish crossover');
        } else if (TechnicalAnalysis.crossunder(macd.macd, macd.signal)) {
          sellSignals += 3;
          totalWeight += 3;
          reasons.push('MACD bearish crossover');
        } else {
          totalWeight += 1;
        }
      }
    }

    // Bollinger Bands signals
    if (prices.close.length >= 20) {
      const bb = this.ta.bollingerBands(prices.close, 20, 2);
      const currentPrice = prices.close[prices.close.length - 1];
      const currentUpper = TechnicalAnalysis.latest(bb.upper);
      const currentLower = TechnicalAnalysis.latest(bb.lower);
      const currentMiddle = TechnicalAnalysis.latest(bb.middle);
      
      if (currentUpper && currentLower && currentMiddle) {
        indicators.bbUpper = currentUpper;
        indicators.bbLower = currentLower;
        indicators.bbMiddle = currentMiddle;
        
        const bbPercent = (currentPrice - currentLower) / (currentUpper - currentLower);
        indicators.bbPercent = bbPercent;
        
        if (bbPercent < 0.2) {
          buySignals += 2;
          totalWeight += 2;
          reasons.push('Near lower Bollinger Band');
        } else if (bbPercent > 0.8) {
          sellSignals += 2;
          totalWeight += 2;
          reasons.push('Near upper Bollinger Band');
        } else {
          totalWeight += 1;
        }
      }
    }

    // Stochastic signals
    if (prices.high.length >= 14 && prices.low.length >= 14 && prices.close.length >= 14) {
      const stoch = this.ta.stochastic(prices.high, prices.low, prices.close, 14, 3, 3);
      const currentK = TechnicalAnalysis.latest(stoch.k);
      const currentD = TechnicalAnalysis.latest(stoch.d);
      
      if (currentK !== undefined && currentD !== undefined) {
        indicators.stochK = currentK;
        indicators.stochD = currentD;
        
        if (currentK < 20 && currentD < 20) {
          buySignals += 1;
          totalWeight += 1;
          reasons.push('Stochastic oversold');
        } else if (currentK > 80 && currentD > 80) {
          sellSignals += 1;
          totalWeight += 1;
          reasons.push('Stochastic overbought');
        } else {
          totalWeight += 1;
        }
      }
    }

    // Determine overall signal
    let action: 'BUY' | 'SELL' | 'HOLD' = 'HOLD';
    let strength = 0;
    
    if (totalWeight > 0) {
      const buyStrength = buySignals / totalWeight;
      const sellStrength = sellSignals / totalWeight;
      
      if (buyStrength > 0.5) {
        action = 'BUY';
        strength = buyStrength;
      } else if (sellStrength > 0.5) {
        action = 'SELL';
        strength = sellStrength;
      } else {
        action = 'HOLD';
        strength = Math.max(buyStrength, sellStrength);
      }
    }

    return {
      symbol,
      timestamp,
      action,
      strength,
      indicators,
      reason: reasons.join('; ') || 'No clear signal'
    };
  }
}