import { BaseStrategy, Signal } from '../BaseStrategy';
import { MarketData } from '../../types';
import { IndicatorManager } from '../indicators/IndicatorManager';
import { PositionManager } from '../position/PositionManager';
import { RiskManager } from '../risk/RiskManager';
import { SignalManager, CommonRules, CommonFilters } from '../signals/SignalManager';
import { getLogger } from '@stock-bot/logger';

const logger = getLogger('AdvancedMultiIndicatorStrategy');

export interface AdvancedStrategyConfig {
  // Indicator settings
  fastMA?: number;
  slowMA?: number;
  rsiPeriod?: number;
  atrPeriod?: number;
  
  // Risk settings
  riskPerTrade?: number;
  maxPositions?: number;
  maxDrawdown?: number;
  useATRStops?: boolean;
  atrMultiplier?: number;
  
  // Signal settings
  signalAggregation?: 'weighted' | 'majority' | 'unanimous' | 'threshold';
  minSignalStrength?: number;
  minSignalConfidence?: number;
  
  // Position sizing
  positionSizing?: 'fixed' | 'risk' | 'kelly' | 'volatility';
  
  // Other
  debugMode?: boolean;
}

/**
 * Advanced strategy using multiple indicators, risk management, and signal aggregation
 */
export class AdvancedMultiIndicatorStrategy extends BaseStrategy {
  private indicatorManager: IndicatorManager;
  private positionManager: PositionManager;
  private riskManager: RiskManager;
  private signalManager: SignalManager;
  
  private config: Required<AdvancedStrategyConfig>;
  private stopLosses: Map<string, number> = new Map();
  private takeProfits: Map<string, number> = new Map();
  
  constructor(strategyConfig: any, modeManager?: any, executionService?: any) {
    super(strategyConfig, modeManager, executionService);
    
    // Initialize config
    this.config = {
      fastMA: 20,
      slowMA: 50,
      rsiPeriod: 14,
      atrPeriod: 14,
      riskPerTrade: 0.02,
      maxPositions: 5,
      maxDrawdown: 0.2,
      useATRStops: true,
      atrMultiplier: 2,
      signalAggregation: 'weighted',
      minSignalStrength: 0.5,
      minSignalConfidence: 0.3,
      positionSizing: 'risk',
      debugMode: false,
      ...strategyConfig.params
    };
    
    // Initialize managers
    const initialCapital = strategyConfig.initialCapital || 100000;
    this.indicatorManager = new IndicatorManager();
    this.positionManager = new PositionManager(initialCapital);
    this.riskManager = new RiskManager(initialCapital, {
      maxPositions: this.config.maxPositions,
      maxDrawdownPct: this.config.maxDrawdown,
      maxPositionSizePct: 0.1
    });
    
    this.signalManager = new SignalManager({
      method: this.config.signalAggregation
    });
    
    // Setup signal rules
    this.setupSignalRules();
    
    logger.info('AdvancedMultiIndicatorStrategy initialized:', this.config);
  }
  
  private setupSignalRules(): void {
    // Moving average rules
    this.signalManager.addRule({
      name: `MA Crossover (${this.config.fastMA}/${this.config.slowMA})`,
      condition: (indicators) => {
        const fast = indicators[`sma${this.config.fastMA}`];
        const slow = indicators[`sma${this.config.slowMA}`];
        const prevFast = indicators[`sma${this.config.fastMA}_prev`];
        const prevSlow = indicators[`sma${this.config.slowMA}_prev`];
        
        if (!fast || !slow || !prevFast || !prevSlow) return false;
        
        // Check for crossover
        const crossover = prevFast <= prevSlow && fast > slow;
        const crossunder = prevFast >= prevSlow && fast < slow;
        
        indicators._maCrossDirection = crossover ? 'up' : crossunder ? 'down' : null;
        return crossover || crossunder;
      },
      weight: 1,
      direction: 'both'
    });
    
    // RSI rules
    this.signalManager.addRules([
      CommonRules.rsiOversold(30),
      CommonRules.rsiOverbought(70)
    ]);
    
    // MACD rules
    this.signalManager.addRules([
      CommonRules.macdBullishCross(),
      CommonRules.macdBearishCross()
    ]);
    
    // Bollinger Band rules
    this.signalManager.addRules([
      CommonRules.priceAtLowerBand(),
      CommonRules.priceAtUpperBand(),
      CommonRules.bollingerSqueeze()
    ]);
    
    // Add filters
    this.signalManager.addFilter(CommonFilters.minStrength(this.config.minSignalStrength));
    this.signalManager.addFilter(CommonFilters.minConfidence(this.config.minSignalConfidence));
    this.signalManager.addFilter(CommonFilters.trendAlignment('sma200'));
  }
  
  protected updateIndicators(data: MarketData): void {
    if (data.type !== 'bar') return;
    
    const { symbol, timestamp, open, high, low, close, volume } = data.data;
    
    // First time setup for symbol
    if (!this.indicatorManager.getHistoryLength(symbol)) {
      this.setupSymbolIndicators(symbol);
    }
    
    // Update price history
    this.indicatorManager.updatePrice({
      symbol,
      timestamp,
      open,
      high,
      low,
      close,
      volume
    });
    
    // Update position market values
    this.updatePositionValues(symbol, close);
    
    // Check stop losses and take profits
    this.checkExitConditions(symbol, close);
  }
  
  private setupSymbolIndicators(symbol: string): void {
    // Setup incremental indicators
    this.indicatorManager.setupIncrementalIndicator(symbol, 'fast_sma', {
      type: 'sma',
      period: this.config.fastMA
    });
    
    this.indicatorManager.setupIncrementalIndicator(symbol, 'slow_sma', {
      type: 'sma',
      period: this.config.slowMA
    });
    
    this.indicatorManager.setupIncrementalIndicator(symbol, 'rsi', {
      type: 'rsi',
      period: this.config.rsiPeriod
    });
    
    logger.info(`Initialized indicators for ${symbol}`);
  }
  
  protected async generateSignal(data: MarketData): Promise<Signal | null> {
    if (data.type !== 'bar') return null;
    
    const { symbol, timestamp, close } = data.data;
    const historyLength = this.indicatorManager.getHistoryLength(symbol);
    
    // Need enough data
    if (historyLength < Math.max(this.config.slowMA, 26)) {
      return null;
    }
    
    // Prepare indicators for signal generation
    const indicators = this.prepareIndicators(symbol, close);
    if (!indicators) return null;
    
    // Check risk before generating signals
    const currentPositions = this.getCurrentPositionMap();
    const riskCheck = this.riskManager.checkNewPosition(
      symbol,
      100, // Dummy size for check
      close,
      currentPositions
    );
    
    if (!riskCheck.allowed && !this.positionManager.hasPosition(symbol)) {
      if (this.config.debugMode) {
        logger.warn(`Risk check failed for ${symbol}: ${riskCheck.reason}`);
      }
      return null;
    }
    
    // Generate trading signal
    const tradingSignal = this.signalManager.generateSignal(
      symbol,
      timestamp,
      indicators,
      { position: this.positionManager.getPositionQuantity(symbol) }
    );
    
    if (!tradingSignal) return null;
    
    // Log signal if in debug mode
    if (this.config.debugMode) {
      logger.info(`Signal generated for ${symbol}:`, {
        direction: tradingSignal.direction,
        strength: tradingSignal.strength.toFixed(2),
        confidence: tradingSignal.confidence.toFixed(2),
        rules: tradingSignal.rules
      });
    }
    
    // Convert to strategy signal
    return this.convertToStrategySignal(tradingSignal, symbol, close);
  }
  
  private prepareIndicators(symbol: string, currentPrice: number): Record<string, number> | null {
    const indicators: Record<string, number> = { price: currentPrice };
    
    // Get moving averages
    const fastMA = this.indicatorManager.getSMA(symbol, this.config.fastMA);
    const slowMA = this.indicatorManager.getSMA(symbol, this.config.slowMA);
    const sma200 = this.indicatorManager.getSMA(symbol, 200);
    
    if (!fastMA || !slowMA) return null;
    
    // Current and previous values
    indicators[`sma${this.config.fastMA}`] = this.indicatorManager.getLatest(fastMA)!;
    indicators[`sma${this.config.slowMA}`] = this.indicatorManager.getLatest(slowMA)!;
    
    if (fastMA.length >= 2 && slowMA.length >= 2) {
      indicators[`sma${this.config.fastMA}_prev`] = fastMA[fastMA.length - 2];
      indicators[`sma${this.config.slowMA}_prev`] = slowMA[slowMA.length - 2];
    }
    
    if (sma200) {
      indicators.sma200 = this.indicatorManager.getLatest(sma200)!;
    }
    
    // RSI
    const rsi = this.indicatorManager.getRSI(symbol, this.config.rsiPeriod);
    if (rsi) {
      indicators.rsi = this.indicatorManager.getLatest(rsi)!;
    }
    
    // MACD
    const macd = this.indicatorManager.getMACD(symbol);
    if (macd) {
      indicators.macd = this.indicatorManager.getLatest(macd.macd)!;
      indicators.macd_signal = this.indicatorManager.getLatest(macd.signal)!;
      indicators.macd_histogram = this.indicatorManager.getLatest(macd.histogram)!;
      
      if (macd.macd.length >= 2) {
        indicators.macd_prev = macd.macd[macd.macd.length - 2];
        indicators.macd_signal_prev = macd.signal[macd.signal.length - 2];
      }
    }
    
    // Bollinger Bands
    const bb = this.indicatorManager.getBollingerBands(symbol);
    if (bb) {
      indicators.bb_upper = this.indicatorManager.getLatest(bb.upper)!;
      indicators.bb_middle = this.indicatorManager.getLatest(bb.middle)!;
      indicators.bb_lower = this.indicatorManager.getLatest(bb.lower)!;
    }
    
    // ATR for volatility
    const atr = this.indicatorManager.getATR(symbol, this.config.atrPeriod);
    if (atr) {
      indicators.atr = this.indicatorManager.getLatest(atr)!;
    }
    
    // Volume
    const priceHistory = this.indicatorManager.getPriceHistory(symbol);
    if (priceHistory) {
      indicators.volume = priceHistory.volume[priceHistory.volume.length - 1];
      const avgVolume = priceHistory.volume.slice(-20).reduce((a, b) => a + b, 0) / 20;
      indicators.avg_volume = avgVolume;
    }
    
    return indicators;
  }
  
  private convertToStrategySignal(
    tradingSignal: TradingSignal,
    symbol: string,
    currentPrice: number
  ): Signal | null {
    const currentPosition = this.positionManager.getPositionQuantity(symbol);
    
    // Determine action based on signal and current position
    let type: 'buy' | 'sell' | 'close';
    let quantity: number;
    
    if (tradingSignal.direction === 'buy') {
      if (currentPosition < 0) {
        // Close short position
        type = 'buy';
        quantity = Math.abs(currentPosition);
      } else if (currentPosition === 0) {
        // Open long position
        type = 'buy';
        quantity = this.calculatePositionSize(symbol, currentPrice, tradingSignal);
      } else {
        // Already long
        return null;
      }
    } else if (tradingSignal.direction === 'sell') {
      if (currentPosition > 0) {
        // Close long position
        type = 'sell';
        quantity = currentPosition;
      } else if (currentPosition === 0 && false) { // Disable shorting for now
        // Open short position
        type = 'sell';
        quantity = this.calculatePositionSize(symbol, currentPrice, tradingSignal);
      } else {
        return null;
      }
    } else {
      return null;
    }
    
    return {
      type,
      symbol,
      strength: tradingSignal.strength,
      reason: tradingSignal.rules.join(', '),
      metadata: {
        ...tradingSignal.indicators,
        quantity,
        confidence: tradingSignal.confidence,
        rules: tradingSignal.rules
      }
    };
  }
  
  private calculatePositionSize(
    symbol: string,
    price: number,
    signal: TradingSignal
  ): number {
    const accountBalance = this.positionManager.getAccountBalance();
    
    switch (this.config.positionSizing) {
      case 'fixed':
        // Fixed percentage of account
        const fixedValue = accountBalance * this.config.riskPerTrade * 5;
        return Math.floor(fixedValue / price);
        
      case 'risk':
        // Risk-based sizing with ATR stop
        const atr = signal.indicators.atr;
        if (atr && this.config.useATRStops) {
          const stopDistance = atr * this.config.atrMultiplier;
          return this.positionManager.calculatePositionSize({
            accountBalance,
            riskPerTrade: this.config.riskPerTrade,
            stopLossDistance: stopDistance
          }, price);
        }
        break;
        
      case 'kelly':
        // Kelly criterion based on historical performance
        const metrics = this.positionManager.getPerformanceMetrics();
        if (metrics.totalTrades >= 20) {
          return this.positionManager.calculateKellySize(
            metrics.winRate / 100,
            metrics.avgWin,
            metrics.avgLoss,
            price
          );
        }
        break;
        
      case 'volatility':
        // Volatility-adjusted sizing
        const atrVol = signal.indicators.atr;
        if (atrVol) {
          return this.positionManager.calculatePositionSize({
            accountBalance,
            riskPerTrade: this.config.riskPerTrade,
            volatilityAdjustment: true,
            atr: atrVol
          }, price);
        }
        break;
    }
    
    // Default sizing
    return this.positionManager.calculatePositionSize({
      accountBalance,
      riskPerTrade: this.config.riskPerTrade
    }, price);
  }
  
  private updatePositionValues(symbol: string, currentPrice: number): void {
    const prices = new Map([[symbol, currentPrice]]);
    this.positionManager.updateMarketPrices(prices);
  }
  
  private checkExitConditions(symbol: string, currentPrice: number): void {
    const position = this.positionManager.getPosition(symbol);
    if (!position) return;
    
    const stopLoss = this.stopLosses.get(symbol);
    const takeProfit = this.takeProfits.get(symbol);
    
    // Check stop loss
    if (stopLoss) {
      if ((position.quantity > 0 && currentPrice <= stopLoss) ||
          (position.quantity < 0 && currentPrice >= stopLoss)) {
        logger.info(`Stop loss triggered for ${symbol} at ${currentPrice}`);
        this.emit('signal', {
          type: 'close',
          symbol,
          strength: 1,
          reason: 'Stop loss triggered',
          metadata: { stopLoss, currentPrice }
        });
      }
    }
    
    // Check take profit
    if (takeProfit) {
      if ((position.quantity > 0 && currentPrice >= takeProfit) ||
          (position.quantity < 0 && currentPrice <= takeProfit)) {
        logger.info(`Take profit triggered for ${symbol} at ${currentPrice}`);
        this.emit('signal', {
          type: 'close',
          symbol,
          strength: 1,
          reason: 'Take profit triggered',
          metadata: { takeProfit, currentPrice }
        });
      }
    }
  }
  
  private getCurrentPositionMap(): Map<string, { quantity: number; value: number }> {
    const positionMap = new Map();
    
    for (const position of this.positionManager.getOpenPositions()) {
      positionMap.set(position.symbol, {
        quantity: position.quantity,
        value: Math.abs(position.quantity * (position.currentPrice || position.avgPrice))
      });
    }
    
    return positionMap;
  }
  
  protected async onOrderUpdate(update: any): Promise<void> {
    await super.onOrderUpdate(update);
    
    if (update.status === 'filled' && update.fills?.length > 0) {
      for (const fill of update.fills) {
        const trade = {
          symbol: update.symbol,
          side: update.side as 'buy' | 'sell',
          quantity: fill.quantity,
          price: fill.price,
          commission: fill.commission || 0,
          timestamp: new Date(fill.timestamp)
        };
        
        const position = this.positionManager.updatePosition(trade);
        
        // Update risk manager
        if (trade.pnl) {
          this.riskManager.updateAfterTrade(trade.pnl);
        }
        
        // Set stop loss and take profit for new positions
        if (this.config.useATRStops && position.quantity !== 0) {
          const atr = this.indicatorManager.getATR(update.symbol);
          if (atr) {
            const currentATR = this.indicatorManager.getLatest(atr);
            if (currentATR) {
              const stopDistance = currentATR * this.config.atrMultiplier;
              const profitDistance = currentATR * this.config.atrMultiplier * 2;
              
              if (position.quantity > 0) {
                this.stopLosses.set(update.symbol, fill.price - stopDistance);
                this.takeProfits.set(update.symbol, fill.price + profitDistance);
              } else {
                this.stopLosses.set(update.symbol, fill.price + stopDistance);
                this.takeProfits.set(update.symbol, fill.price - profitDistance);
              }
              
              logger.info(`Set stop/take profit for ${update.symbol}: Stop=${this.stopLosses.get(update.symbol)?.toFixed(2)}, TP=${this.takeProfits.get(update.symbol)?.toFixed(2)}`);
            }
          }
        }
        
        // Clear stops if position closed
        if (position.quantity === 0) {
          this.stopLosses.delete(update.symbol);
          this.takeProfits.delete(update.symbol);
        }
      }
    }
  }
  
  getPerformance(): any {
    const basePerf = super.getPerformance();
    const positionMetrics = this.positionManager.getPerformanceMetrics();
    const riskMetrics = this.riskManager.getMetrics(this.getCurrentPositionMap());
    const signalStats = this.signalManager.getSignalStats();
    
    return {
      ...basePerf,
      ...positionMetrics,
      risk: riskMetrics,
      signals: signalStats
    };
  }
  
  // Daily reset for risk metrics
  onDayEnd(): void {
    this.riskManager.resetDaily();
    logger.info('Daily risk metrics reset');
  }
}