import { IServiceContainer } from '@stock-bot/di';
import { TradingEngine } from '@stock-bot/engine';
import { EventEmitter } from 'events';
import { v4 as uuidv4 } from 'uuid';
import { ModeConfig, OrderRequest, OrderRequestSchema } from '../types';
import { StorageService } from './StorageService';

interface ExecutionReport {
  orderId: string;
  clientOrderId: string;
  symbol: string;
  side: 'buy' | 'sell';
  quantity: number;
  status: 'pending' | 'accepted' | 'partiallyFilled' | 'filled' | 'cancelled' | 'rejected';
  fills: Fill[];
  rejectionReason?: string;
  timestamp: number;
}

interface Fill {
  price: number;
  quantity: number;
  commission: number;
  timestamp: number;
}

export class ExecutionService extends EventEmitter {
  private mode: 'backtest' | 'paper' | 'live' = 'paper';
  private tradingEngine: TradingEngine | null = null;
  private brokerClient: any = null; // Would be specific broker API client
  private pendingOrders = new Map<string, OrderRequest>();
  private container: IServiceContainer;
  private storageService: StorageService;

  constructor(container: IServiceContainer, storageService: StorageService) {
    super();
    this.container = container;
    this.storageService = storageService;
  }

  async initialize(config: ModeConfig, tradingEngine: TradingEngine): Promise<void> {
    this.mode = config.mode;
    this.tradingEngine = tradingEngine;

    if (config.mode === 'live') {
      // Initialize broker connection
      await this.initializeBroker(config.broker, config.accountId);
    }
  }

  private async initializeBroker(broker: string, accountId: string): Promise<void> {
    // In real implementation, would initialize specific broker API
    // For example: Alpaca, Interactive Brokers, etc.
    this.container.logger.info(`Initializing ${broker} broker connection for account ${accountId}`);
  }

  async submitOrder(orderRequest: OrderRequest): Promise<ExecutionReport> {
    // Validate order request
    const validatedOrder = OrderRequestSchema.parse(orderRequest);
    
    // Generate order ID
    const orderId = uuidv4();
    const clientOrderId = validatedOrder.clientOrderId || orderId;
    
    // Store pending order
    this.pendingOrders.set(orderId, validatedOrder);
    
    try {
      // Check risk before submitting
      const riskResult = await this.checkRisk(validatedOrder);
      if (!riskResult.passed) {
        return this.createRejectionReport(
          orderId,
          clientOrderId,
          validatedOrder,
          `Risk check failed: ${riskResult.violations.join(', ')}`
        );
      }

      // Submit based on mode
      let result: ExecutionReport;
      
      switch (this.mode) {
        case 'backtest':
        case 'paper':
          result = await this.submitToSimulation(orderId, clientOrderId, validatedOrder);
          break;
        case 'live':
          result = await this.submitToBroker(orderId, clientOrderId, validatedOrder);
          break;
      }
      
      // Emit order event
      this.emit('orderUpdate', result);
      
      // If filled, update positions
      if (result.fills.length > 0) {
        await this.processFills(result);
      }
      
      return result;
      
    } catch (error) {
      this.container.logger.error('Error submitting order:', error);
      return this.createRejectionReport(
        orderId,
        clientOrderId,
        validatedOrder,
        error instanceof Error ? error.message : 'Unknown error'
      );
    }
  }

  private async checkRisk(order: OrderRequest): Promise<any> {
    if (!this.tradingEngine) {
      throw new Error('Trading engine not initialized');
    }

    // Convert to engine format
    const engineOrder = {
      id: uuidv4(),
      symbol: order.symbol,
      side: order.side,
      quantity: order.quantity,
      orderType: order.orderType,
      limitPrice: order.limitPrice,
      timeInForce: order.timeInForce
    };

    const result = this.tradingEngine.checkRisk(engineOrder);
    return JSON.parse(result);
  }

  private async submitToSimulation(
    orderId: string,
    clientOrderId: string,
    order: OrderRequest
  ): Promise<ExecutionReport> {
    if (!this.tradingEngine) {
      throw new Error('Trading engine not initialized');
    }

    // Convert to engine format
    const engineOrder = {
      id: orderId,
      symbol: order.symbol,
      side: order.side,
      quantity: order.quantity,
      orderType: order.orderType,
      limitPrice: order.limitPrice,
      timeInForce: order.timeInForce
    };

    // Submit to engine
    const result = await this.tradingEngine.submitOrder(engineOrder);
    const engineResult = JSON.parse(result);

    // Convert back to our format
    return {
      orderId,
      clientOrderId,
      symbol: order.symbol,
      side: order.side,
      quantity: order.quantity,
      status: this.mapEngineStatus(engineResult.status),
      fills: engineResult.fills || [],
      timestamp: Date.now()
    };
  }

  private async submitToBroker(
    orderId: string,
    clientOrderId: string,
    order: OrderRequest
  ): Promise<ExecutionReport> {
    // In real implementation, would submit to actual broker
    // This is a placeholder
    this.container.logger.info(`Submitting order ${orderId} to broker`);
    
    // Simulate broker response
    return {
      orderId,
      clientOrderId,
      symbol: order.symbol,
      side: order.side,
      quantity: order.quantity,
      status: 'pending',
      fills: [],
      timestamp: Date.now()
    };
  }

  async cancelOrder(orderId: string): Promise<boolean> {
    const order = this.pendingOrders.get(orderId);
    if (!order) {
      this.container.logger.warn(`Order ${orderId} not found`);
      return false;
    }

    try {
      switch (this.mode) {
        case 'backtest':
        case 'paper':
          // Cancel in simulation
          if (this.tradingEngine) {
            await this.tradingEngine.cancelOrder(orderId);
          }
          break;
        case 'live':
          // Cancel with broker
          if (this.brokerClient) {
            await this.brokerClient.cancelOrder(orderId);
          }
          break;
      }

      this.pendingOrders.delete(orderId);
      
      // Emit cancellation event
      this.emit('orderUpdate', {
        orderId,
        status: 'cancelled',
        timestamp: Date.now()
      });
      
      return true;
      
    } catch (error) {
      this.container.logger.error(`Error cancelling order ${orderId}:`, error);
      return false;
    }
  }

  private async processFills(executionReport: ExecutionReport): Promise<void> {
    if (!this.tradingEngine) {return;}

    for (const fill of executionReport.fills) {
      // Update position in engine
      const result = this.tradingEngine.processFill(
        executionReport.symbol,
        fill.price,
        fill.quantity,
        executionReport.side,
        fill.commission
      );
      
      // Emit fill event
      this.emit('fill', {
        orderId: executionReport.orderId,
        symbol: executionReport.symbol,
        side: executionReport.side,
        ...fill,
        positionUpdate: JSON.parse(result)
      });
    }
  }

  private createRejectionReport(
    orderId: string,
    clientOrderId: string,
    order: OrderRequest,
    reason: string
  ): ExecutionReport {
    return {
      orderId,
      clientOrderId,
      symbol: order.symbol,
      side: order.side,
      quantity: order.quantity,
      status: 'rejected',
      fills: [],
      rejectionReason: reason,
      timestamp: Date.now()
    };
  }

  private mapEngineStatus(engineStatus: string): ExecutionReport['status'] {
    const statusMap: Record<string, ExecutionReport['status']> = {
      'Pending': 'pending',
      'Accepted': 'accepted',
      'PartiallyFilled': 'partiallyFilled',
      'Filled': 'filled',
      'Cancelled': 'cancelled',
      'Rejected': 'rejected'
    };
    
    return statusMap[engineStatus] || 'rejected';
  }

  async routeOrderToExchange(order: OrderRequest, exchange: string): Promise<void> {
    // This would route orders to specific exchanges in live mode
    // For now, just a placeholder
    this.container.logger.info(`Routing order to ${exchange}:`, order);
  }

  async getOrderStatus(orderId: string): Promise<ExecutionReport | null> {
    // In real implementation, would query broker or internal state
    return null;
  }

  async shutdown(): Promise<void> {
    // Cancel all pending orders
    for (const orderId of this.pendingOrders.keys()) {
      await this.cancelOrder(orderId);
    }
    
    // Disconnect from broker
    if (this.brokerClient) {
      // await this.brokerClient.disconnect();
      this.brokerClient = null;
    }
    
    this.tradingEngine = null;
    this.removeAllListeners();
  }
}