import { v4 as uuidv4 } from 'uuid';
import { getLogger } from '@stock-bot/logger';
import type { IServiceContainer } from '@stock-bot/handlers';

const logger = getLogger('backtest-service');

// Use environment variable or default
const ORCHESTRATOR_URL = process.env.ORCHESTRATOR_URL || 'http://localhost:2004';

export interface BacktestRequest {
  strategy: string;
  symbols: string[];
  startDate: string;
  endDate: string;
  initialCapital: number;
  config?: Record<string, any>;
}

export interface BacktestJob {
  id: string;
  status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
  strategy: string;
  symbols: string[];
  startDate: Date;
  endDate: Date;
  initialCapital: number;
  config: Record<string, any>;
  createdAt: Date;
  updatedAt: Date;
  error?: string;
}

export class BacktestService {
  private container: IServiceContainer;

  constructor(container: IServiceContainer) {
    this.container = container;
    logger.info('BacktestService initialized', {
      hasPostgres: !!container.postgres,
      containerKeys: Object.keys(container)
    });
  }
  async createBacktest(request: BacktestRequest): Promise<BacktestJob> {
    const backtestId = uuidv4();
    
    // Insert into PostgreSQL
    const result = await this.container.postgres.query(
      `INSERT INTO backtests 
       (id, name, strategy, symbols, start_date, end_date, initial_capital, config, status)
       VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 'pending')
       RETURNING *`,
      [
        backtestId,
        request.config?.name || `Backtest ${new Date().toISOString()}`,
        request.strategy,
        JSON.stringify(request.symbols),
        request.startDate,
        request.endDate,
        request.initialCapital,
        JSON.stringify(request.config || {})
      ]
    );

    const backtest: BacktestJob = {
      id: result.rows[0].id,
      status: result.rows[0].status,
      strategy: result.rows[0].strategy,
      symbols: result.rows[0].symbols,
      startDate: result.rows[0].start_date,
      endDate: result.rows[0].end_date,
      initialCapital: parseFloat(result.rows[0].initial_capital),
      config: result.rows[0].config,
      createdAt: result.rows[0].created_at,
      updatedAt: result.rows[0].updated_at,
    };
    
    // Call orchestrator to run backtest
    try {
      const response = await fetch(`${ORCHESTRATOR_URL}/api/backtest/run`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          mode: 'backtest',
          startDate: new Date(request.startDate).toISOString(),
          endDate: new Date(request.endDate).toISOString(),
          symbols: request.symbols,
          strategy: request.strategy,
          initialCapital: request.initialCapital,
          dataFrequency: '1d', // Default to daily
          commission: request.config?.commission,
          slippage: request.config?.slippage,
          speed: 'max', // Default speed
          fillModel: {
            slippage: 'realistic',
            marketImpact: true,
            partialFills: true
          }
        }),
      });

      if (!response.ok) {
        throw new Error(`Orchestrator returned ${response.status}`);
      }

      const result = await response.json();
      
      // Store result directly without transformation
      if (result.status === 'completed') {
        // Update backtest status
        await this.container.postgres.query(
          'UPDATE backtests SET status = $1, updated_at = NOW() WHERE id = $2',
          ['completed', backtestId]
        );
        
        // Store results in backtest_results table
        await this.saveBacktestResults(backtestId, result);
        
        logger.info('Backtest completed', { 
          backtestId, 
          trades: result.metrics?.totalTrades,
          return: result.metrics?.totalReturn 
        });
      } else {
        // Update status to running if not completed
        await this.container.postgres.query(
          'UPDATE backtests SET status = $1, updated_at = NOW() WHERE id = $2',
          ['running', backtestId]
        );
      }
      
      logger.info('Backtest started in orchestrator', { backtestId, result });
    } catch (error) {
      logger.error('Failed to start backtest in orchestrator', { backtestId, error });
      await this.container.postgres.query(
        'UPDATE backtests SET status = $1, error = $2, updated_at = NOW() WHERE id = $3',
        ['failed', error.message, backtestId]
      );
      backtest.status = 'failed';
      backtest.error = error.message;
    }
    
    return backtest;
  }

  async getBacktest(id: string): Promise<BacktestJob | null> {
    const result = await this.container.postgres.query(
      'SELECT * FROM backtests WHERE id = $1',
      [id]
    );
    
    if (result.rows.length === 0) {
      return null;
    }
    
    const row = result.rows[0];
    return {
      id: row.id,
      status: row.status,
      strategy: row.strategy,
      symbols: row.symbols,
      startDate: row.start_date,
      endDate: row.end_date,
      initialCapital: parseFloat(row.initial_capital),
      config: row.config,
      createdAt: row.created_at,
      updatedAt: row.updated_at,
      error: row.error,
    };
  }

  async getBacktestResults(id: string): Promise<any> {
    const result = await this.container.postgres.query(
      `SELECT 
        br.*,
        b.strategy,
        b.symbols,
        b.start_date,
        b.end_date,
        b.initial_capital,
        b.config as backtest_config
      FROM backtest_results br
      JOIN backtests b ON b.id = br.backtest_id
      WHERE br.backtest_id = $1`,
      [id]
    );
    
    if (result.rows.length === 0) {
      return null;
    }
    
    const row = result.rows[0];
    
    // Reconstruct the result format expected by frontend
    return {
      backtestId: row.backtest_id,
      status: 'completed',
      completedAt: row.completed_at.toISOString(),
      config: {
        name: row.backtest_config?.name || 'Backtest',
        strategy: row.strategy,
        symbols: row.symbols,
        startDate: row.start_date.toISOString(),
        endDate: row.end_date.toISOString(),
        initialCapital: parseFloat(row.initial_capital),
        commission: row.backtest_config?.commission ?? 0.001,
        slippage: row.backtest_config?.slippage ?? 0.0001,
        dataFrequency: row.backtest_config?.dataFrequency || '1d',
      },
      metrics: row.metrics,
      equity: row.equity_curve,
      ohlcData: row.ohlc_data,
      trades: row.trades,
      positions: row.positions,
      analytics: row.analytics,
      executionTime: row.execution_time,
    };
  }

  async listBacktests(params: { limit: number; offset: number }): Promise<BacktestJob[]> {
    const result = await this.container.postgres.query(
      `SELECT * FROM backtests 
       ORDER BY created_at DESC 
       LIMIT $1 OFFSET $2`,
      [params.limit, params.offset]
    );
    
    return result.rows.map(row => ({
      id: row.id,
      status: row.status,
      strategy: row.strategy,
      symbols: row.symbols,
      startDate: row.start_date,
      endDate: row.end_date,
      initialCapital: parseFloat(row.initial_capital),
      config: row.config,
      createdAt: row.created_at,
      updatedAt: row.updated_at,
      error: row.error,
    }));
  }

  async updateBacktestStatus(id: string, status: BacktestJob['status'], error?: string): Promise<void> {
    if (error) {
      await this.container.postgres.query(
        'UPDATE backtests SET status = $1, error = $2, updated_at = NOW() WHERE id = $3',
        [status, error, id]
      );
    } else {
      await this.container.postgres.query(
        'UPDATE backtests SET status = $1, updated_at = NOW() WHERE id = $2',
        [status, id]
      );
    }
  }

  async cancelBacktest(id: string): Promise<void> {
    await this.updateBacktestStatus(id, 'cancelled');
    
    // Call orchestrator to stop backtest
    try {
      await fetch(`${ORCHESTRATOR_URL}/api/backtest/stop`, {
        method: 'POST',
      });
    } catch (error) {
      logger.error('Failed to stop backtest in orchestrator', { backtestId: id, error });
    }
  }

  private async saveBacktestResults(backtestId: string, result: any): Promise<void> {
    try {
      await this.container.postgres.query(
        `INSERT INTO backtest_results 
         (backtest_id, completed_at, metrics, equity_curve, ohlc_data, trades, positions, analytics, execution_time)
         VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
        [
          backtestId,
          result.completedAt || new Date(),
          JSON.stringify(result.metrics || {}),
          JSON.stringify(result.equity || result.equityCurve || []),
          JSON.stringify(result.ohlcData || {}),
          JSON.stringify(result.trades || []),
          JSON.stringify(result.positions || result.finalPositions || {}),
          JSON.stringify(result.analytics || {}),
          result.executionTime || 0
        ]
      );
    } catch (error) {
      logger.error('Failed to save backtest results', { backtestId, error });
      throw error;
    }
  }
}