import { EventEmitter } from 'events';
import { getLogger } from '@stock-bot/logger';

const logger = getLogger('BaseStrategy');
import { MarketData, StrategyConfig, OrderRequest } from '../types';
import { ModeManager } from '../core/ModeManager';
import { ExecutionService } from '../services/ExecutionService';

export interface Signal {
  type: 'buy' | 'sell' | 'close';
  symbol: string;
  strength: number; // -1 to 1
  reason?: string;
  metadata?: Record<string, any>;
}

export abstract class BaseStrategy extends EventEmitter {
  protected config: StrategyConfig;
  protected isActive = false;
  protected positions = new Map<string, number>();
  protected pendingOrders = new Map<string, OrderRequest>();
  protected performance = {
    trades: 0,
    wins: 0,
    losses: 0,
    totalPnl: 0,
    maxDrawdown: 0,
    currentDrawdown: 0,
    peakEquity: 0
  };

  constructor(
    config: StrategyConfig,
    protected modeManager: ModeManager,
    protected executionService: ExecutionService
  ) {
    super();
    this.config = config;
  }

  async initialize(): Promise<void> {
    logger.info(`Initializing strategy: ${this.config.name}`);
    // Subscribe to symbols
    for (const symbol of this.config.symbols) {
      // Note: In real implementation, would subscribe through market data service
      logger.debug(`Strategy ${this.config.id} subscribed to ${symbol}`);
    }
  }

  async start(): Promise<void> {
    this.isActive = true;
    logger.info(`Started strategy: ${this.config.name}`);
    this.onStart();
  }

  async stop(): Promise<void> {
    this.isActive = false;
    
    // Cancel pending orders
    for (const [orderId, order] of this.pendingOrders) {
      await this.executionService.cancelOrder(orderId);
    }
    this.pendingOrders.clear();
    
    logger.info(`Stopped strategy: ${this.config.name}`);
    this.onStop();
  }

  async shutdown(): Promise<void> {
    await this.stop();
    this.removeAllListeners();
    logger.info(`Shutdown strategy: ${this.config.name}`);
  }

  // Market data handling
  async onMarketData(data: MarketData): Promise<void> {
    if (!this.isActive) {
      logger.info(`[BaseStrategy] Strategy ${this.config.id} received market data but is not active`);
      return;
    }
    
    try {
      // Update any indicators or state
      this.updateIndicators(data);
      
      // Generate signals
      const signal = await this.generateSignal(data);
      
      if (signal) {
        this.emit('signal', signal);
        
        // Convert signal to order if strong enough
        const order = await this.signalToOrder(signal);
        if (order) {
          this.emit('order', order);
        }
      }
    } catch (error) {
      logger.error(`Strategy ${this.config.id} error:`, error);
    }
  }

  async onMarketDataBatch(batch: MarketData[]): Promise<void> {
    // Default implementation processes individually
    // Strategies can override for more efficient batch processing
    for (const data of batch) {
      await this.onMarketData(data);
    }
  }

  // Order and fill handling
  async onOrderUpdate(update: any): Promise<void> {
    logger.debug(`Strategy ${this.config.id} order update:`, update);
    
    if (update.status === 'filled') {
      // Remove from pending
      this.pendingOrders.delete(update.orderId);
      
      // Update position tracking
      const fill = update.fills[0]; // Assuming single fill for simplicity
      if (fill) {
        const currentPos = this.positions.get(update.symbol) || 0;
        const newPos = update.side === 'buy' 
          ? currentPos + fill.quantity 
          : currentPos - fill.quantity;
        
        logger.info(`[BaseStrategy] Position update for ${update.symbol}: ${currentPos} -> ${newPos} (${update.side} ${fill.quantity})`);
        
        if (Math.abs(newPos) < 0.0001) {
          this.positions.delete(update.symbol);
        } else {
          this.positions.set(update.symbol, newPos);
        }
      }
    } else if (update.status === 'rejected' || update.status === 'cancelled') {
      this.pendingOrders.delete(update.orderId);
    }
  }

  async onOrderError(order: OrderRequest, error: any): Promise<void> {
    logger.error(`Strategy ${this.config.id} order error:`, error);
    // Strategies can override to handle errors
  }

  async onFill(fill: any): Promise<void> {
    // Update performance metrics
    this.performance.trades++;
    
    if (fill.pnl > 0) {
      this.performance.wins++;
    } else if (fill.pnl < 0) {
      this.performance.losses++;
    }
    
    this.performance.totalPnl += fill.pnl;
    
    // Update drawdown
    const currentEquity = this.getEquity();
    if (currentEquity > this.performance.peakEquity) {
      this.performance.peakEquity = currentEquity;
      this.performance.currentDrawdown = 0;
    } else {
      this.performance.currentDrawdown = (this.performance.peakEquity - currentEquity) / this.performance.peakEquity;
      this.performance.maxDrawdown = Math.max(this.performance.maxDrawdown, this.performance.currentDrawdown);
    }
  }

  // Configuration
  async updateConfig(updates: Partial<StrategyConfig>): Promise<void> {
    this.config = { ...this.config, ...updates };
    logger.info(`Updated config for strategy ${this.config.id}`);
    
    // Strategies can override to handle specific config changes
    this.onConfigUpdate(updates);
  }

  // Helper methods
  isInterestedInSymbol(symbol: string): boolean {
    return this.config.symbols.includes(symbol);
  }

  hasPosition(symbol: string): boolean {
    return this.positions.has(symbol) && Math.abs(this.positions.get(symbol)!) > 0.0001;
  }

  getPosition(symbol: string): number {
    return this.positions.get(symbol) || 0;
  }

  getPerformance(): any {
    const winRate = this.performance.trades > 0 
      ? (this.performance.wins / this.performance.trades) * 100 
      : 0;
    
    return {
      ...this.performance,
      winRate,
      averagePnl: this.performance.trades > 0 
        ? this.performance.totalPnl / this.performance.trades 
        : 0
    };
  }

  protected getEquity(): number {
    // Get initial capital from config parameters or default
    const initialCapital = this.config.parameters?.initialCapital || 100000;
    return initialCapital + this.performance.totalPnl;
  }

  protected async signalToOrder(signal: Signal): Promise<OrderRequest | null> {
    // Only act on strong signals
    if (Math.abs(signal.strength) < 0.7) return null;
    
    // Check if we already have a position
    const currentPosition = this.getPosition(signal.symbol);
    
    // Use quantity from signal metadata if provided
    const quantity = signal.metadata?.quantity || this.calculatePositionSize(signal);
    
    logger.info(`[BaseStrategy] Converting signal to order: ${signal.type} ${quantity} ${signal.symbol}, current position: ${currentPosition}`);
    
    // Simple logic - can be overridden by specific strategies
    if (signal.type === 'buy') {
      // Allow buying to open long or close short
      return {
        symbol: signal.symbol,
        side: 'buy',
        quantity: quantity,
        orderType: 'market',
        timeInForce: 'DAY'
      };
    } else if (signal.type === 'sell') {
      // Allow selling to close long or open short
      return {
        symbol: signal.symbol,
        side: 'sell',
        quantity: quantity,
        orderType: 'market',
        timeInForce: 'DAY'
      };
    } else if (signal.type === 'close' && currentPosition !== 0) {
      return {
        symbol: signal.symbol,
        side: currentPosition > 0 ? 'sell' : 'buy',
        quantity: Math.abs(currentPosition),
        orderType: 'market',
        timeInForce: 'DAY'
      };
    }
    
    return null;
  }

  protected calculatePositionSize(signal: Signal): number {
    // Simple fixed size - strategies should override with proper position sizing
    const baseSize = 100; // 100 shares
    const allocation = this.config.allocation || 1.0;
    
    return Math.floor(baseSize * allocation * Math.abs(signal.strength));
  }

  // Abstract methods that strategies must implement
  protected abstract updateIndicators(data: MarketData): void;
  protected abstract generateSignal(data: MarketData): Promise<Signal | null>;
  
  // Optional hooks for strategies to override
  protected onStart(): void {}
  protected onStop(): void {}
  protected onConfigUpdate(updates: Partial<StrategyConfig>): void {}
}