import { BaseStrategy, Signal } from '../BaseStrategy';
import { MarketData } from '../../types';
import { getLogger } from '@stock-bot/logger';

const logger = getLogger('MeanReversionStrategy');

interface MeanReversionIndicators {
  sma20: number;
  sma50: number;
  stdDev: number;
  zScore: number;
  rsi: number;
}

export class MeanReversionStrategy extends BaseStrategy {
  private priceHistory = new Map<string, number[]>();
  private indicators = new Map<string, MeanReversionIndicators>();
  
  // Strategy parameters
  private readonly LOOKBACK_PERIOD = 20;
  private readonly Z_SCORE_ENTRY = 2.0;
  private readonly Z_SCORE_EXIT = 0.5;
  private readonly RSI_OVERSOLD = 30;
  private readonly RSI_OVERBOUGHT = 70;
  private readonly MIN_VOLUME = 1000000; // $1M daily volume

  protected updateIndicators(data: MarketData): void {
    if (data.type !== 'bar') return;
    
    const symbol = data.data.symbol;
    const price = data.data.close;
    
    // Update price history
    if (!this.priceHistory.has(symbol)) {
      this.priceHistory.set(symbol, []);
    }
    
    const history = this.priceHistory.get(symbol)!;
    history.push(price);
    
    // Keep only needed history
    if (history.length > this.LOOKBACK_PERIOD * 3) {
      history.shift();
    }
    
    // Calculate indicators if we have enough data
    if (history.length >= this.LOOKBACK_PERIOD) {
      const indicators = this.calculateIndicators(history);
      this.indicators.set(symbol, indicators);
    }
  }

  private calculateIndicators(prices: number[]): MeanReversionIndicators {
    const len = prices.length;
    
    // Calculate SMAs
    const sma20 = this.calculateSMA(prices, 20);
    const sma50 = len >= 50 ? this.calculateSMA(prices, 50) : sma20;
    
    // Calculate standard deviation
    const stdDev = this.calculateStdDev(prices.slice(-20), sma20);
    
    // Calculate Z-score
    const currentPrice = prices[len - 1];
    const zScore = stdDev > 0 ? (currentPrice - sma20) / stdDev : 0;
    
    // Calculate RSI
    const rsi = this.calculateRSI(prices, 14);
    
    return { sma20, sma50, stdDev, zScore, rsi };
  }

  protected async generateSignal(data: MarketData): Promise<Signal | null> {
    if (data.type !== 'bar') return null;
    
    const symbol = data.data.symbol;
    const indicators = this.indicators.get(symbol);
    
    if (!indicators) return null;
    
    // Check volume filter
    if (data.data.volume * data.data.close < this.MIN_VOLUME) {
      return null;
    }
    
    const position = this.getPosition(symbol);
    const { zScore, rsi, sma20, sma50 } = indicators;
    
    // Entry signals
    if (position === 0) {
      // Long entry: Oversold conditions
      if (zScore < -this.Z_SCORE_ENTRY && rsi < this.RSI_OVERSOLD && sma20 > sma50) {
        return {
          type: 'buy',
          symbol,
          strength: Math.min(Math.abs(zScore) / 3, 1),
          reason: `Mean reversion long: Z-score ${zScore.toFixed(2)}, RSI ${rsi.toFixed(0)}`,
          metadata: { indicators }
        };
      }
      
      // Short entry: Overbought conditions
      if (zScore > this.Z_SCORE_ENTRY && rsi > this.RSI_OVERBOUGHT && sma20 < sma50) {
        return {
          type: 'sell',
          symbol,
          strength: Math.min(Math.abs(zScore) / 3, 1),
          reason: `Mean reversion short: Z-score ${zScore.toFixed(2)}, RSI ${rsi.toFixed(0)}`,
          metadata: { indicators }
        };
      }
    }
    
    // Exit signals
    if (position > 0) {
      // Exit long: Price reverted to mean or stop loss
      if (zScore > -this.Z_SCORE_EXIT || zScore > this.Z_SCORE_ENTRY) {
        return {
          type: 'close',
          symbol,
          strength: 1,
          reason: `Exit long: Z-score ${zScore.toFixed(2)}`,
          metadata: { indicators }
        };
      }
    } else if (position < 0) {
      // Exit short: Price reverted to mean or stop loss
      if (zScore < this.Z_SCORE_EXIT || zScore < -this.Z_SCORE_ENTRY) {
        return {
          type: 'close',
          symbol,
          strength: 1,
          reason: `Exit short: Z-score ${zScore.toFixed(2)}`,
          metadata: { indicators }
        };
      }
    }
    
    return null;
  }

  private calculateSMA(prices: number[], period: number): number {
    const relevantPrices = prices.slice(-period);
    return relevantPrices.reduce((sum, p) => sum + p, 0) / relevantPrices.length;
  }

  private calculateStdDev(prices: number[], mean: number): number {
    const squaredDiffs = prices.map(p => Math.pow(p - mean, 2));
    const variance = squaredDiffs.reduce((sum, d) => sum + d, 0) / prices.length;
    return Math.sqrt(variance);
  }

  private calculateRSI(prices: number[], period: number = 14): number {
    if (prices.length < period + 1) return 50;
    
    let gains = 0;
    let losses = 0;
    
    // Calculate initial average gain/loss
    for (let i = 1; i <= period; i++) {
      const change = prices[prices.length - i] - prices[prices.length - i - 1];
      if (change > 0) {
        gains += change;
      } else {
        losses += Math.abs(change);
      }
    }
    
    const avgGain = gains / period;
    const avgLoss = losses / period;
    
    if (avgLoss === 0) return 100;
    
    const rs = avgGain / avgLoss;
    const rsi = 100 - (100 / (1 + rs));
    
    return rsi;
  }

  protected onStart(): void {
    logger.info(`Mean Reversion Strategy started with symbols: ${this.config.symbols.join(', ')}`);
  }

  protected onStop(): void {
    logger.info('Mean Reversion Strategy stopped');
    // Clear indicators
    this.priceHistory.clear();
    this.indicators.clear();
  }

  protected onConfigUpdate(updates: any): void {
    logger.info('Mean Reversion Strategy config updated:', updates);
  }
}