/**
 * ML Engine Client
 * Client for communicating with the ML Engine Python service
 *
 * @see apps/ml-engine/MIGRATION_REPORT.md
 */

import axios, { AxiosInstance, AxiosError } from 'axios';
import { logger } from '../utils/logger';

// ============================================================================
// Types
// ============================================================================

export type Timeframe = '1m' | '5m' | '15m' | '30m' | '1h' | '4h' | '1d' | '1w';
export type SignalDirection = 'long' | 'short' | 'neutral';
export type AMDPhase = 'accumulation' | 'manipulation' | 'distribution' | 'unknown';
export type VolatilityRegime = 'low' | 'medium' | 'high' | 'extreme';

export interface MLSignal {
  symbol: string;
  timeframe: Timeframe;
  direction: SignalDirection;
  confidence: number;
  entry_price: number;
  stop_loss: number;
  take_profit: number;
  risk_reward_ratio: number;
  predicted_delta_high: number;
  predicted_delta_low: number;
  prob_tp_first: number;
  amd_phase: AMDPhase;
  volatility_regime: VolatilityRegime;
  timestamp: string;
  metadata?: Record<string, unknown>;
}

export interface SignalRequest {
  symbol: string;
  timeframe?: Timeframe;
  include_features?: boolean;
}

export interface BatchSignalRequest {
  symbols: string[];
  timeframe?: Timeframe;
}

export interface AMDAnalysis {
  symbol: string;
  timeframe: Timeframe;
  current_phase: AMDPhase;
  phase_confidence: number;
  phase_duration_bars: number;
  expected_next_phase: AMDPhase;
  transition_probability: number;
  key_levels: {
    accumulation_low: number;
    manipulation_high: number;
    distribution_target: number;
  };
  recommendations: string[];
}

export interface RangePrediction {
  symbol: string;
  timeframe: Timeframe;
  current_price: number;
  predicted_high: number;
  predicted_low: number;
  delta_high: number;
  delta_low: number;
  confidence_high: number;
  confidence_low: number;
  prediction_horizon: string;
}

export interface BacktestRequest {
  symbol: string;
  timeframe: Timeframe;
  start_date: string;
  end_date: string;
  strategy?: string;
  initial_capital?: number;
  risk_per_trade?: number;
}

export interface BacktestResult {
  symbol: string;
  timeframe: Timeframe;
  period: { start: string; end: string };
  metrics: {
    total_trades: number;
    winning_trades: number;
    losing_trades: number;
    win_rate: number;
    total_return: number;
    total_return_percent: number;
    max_drawdown: number;
    sharpe_ratio: number;
    sortino_ratio: number;
    profit_factor: number;
    avg_trade_duration: string;
  };
  trades: Array<{
    entry_date: string;
    exit_date: string;
    direction: SignalDirection;
    entry_price: number;
    exit_price: number;
    pnl: number;
    pnl_percent: number;
  }>;
  equity_curve: Array<{ date: string; equity: number }>;
}

export interface ModelInfo {
  name: string;
  version: string;
  trained_at: string;
  symbols: string[];
  timeframes: Timeframe[];
  metrics: {
    accuracy: number;
    precision: number;
    recall: number;
    f1_score: number;
  };
}

export interface MLEngineHealthResponse {
  status: 'healthy' | 'unhealthy';
  version: string;
  models_loaded: number;
  gpu_available: boolean;
  uptime_seconds: number;
}

// ============================================================================
// Client Implementation
// ============================================================================

class MLEngineClient {
  private client: AxiosInstance;
  private baseUrl: string;

  constructor() {
    this.baseUrl = process.env.ML_ENGINE_URL || 'http://localhost:8001';

    this.client = axios.create({
      baseURL: this.baseUrl,
      timeout: 60000, // ML operations can be slow
      headers: {
        'Content-Type': 'application/json',
      },
    });

    // Request interceptor
    this.client.interceptors.request.use(
      (config) => {
        logger.debug('[MLEngineClient] Request:', {
          method: config.method?.toUpperCase(),
          url: config.url,
        });
        return config;
      },
      (error) => {
        logger.error('[MLEngineClient] Request error:', error.message);
        return Promise.reject(error);
      }
    );

    // Response interceptor
    this.client.interceptors.response.use(
      (response) => response,
      (error: AxiosError) => {
        const status = error.response?.status;
        const message = (error.response?.data as { detail?: string })?.detail || error.message;

        logger.error('[MLEngineClient] Response error:', {
          status,
          message,
          url: error.config?.url,
        });

        throw new Error(`ML Engine API error: ${message}`);
      }
    );
  }

  // ==========================================================================
  // Health & Status
  // ==========================================================================

  /**
   * Check if ML Engine service is healthy
   */
  async healthCheck(): Promise<MLEngineHealthResponse> {
    const response = await this.client.get<MLEngineHealthResponse>('/health');
    return response.data;
  }

  /**
   * Check if service is available
   */
  async isAvailable(): Promise<boolean> {
    try {
      await this.healthCheck();
      return true;
    } catch {
      return false;
    }
  }

  /**
   * Get loaded models information
   */
  async getModels(): Promise<ModelInfo[]> {
    const response = await this.client.get<{ models: ModelInfo[] }>('/api/v1/models');
    return response.data.models;
  }

  // ==========================================================================
  // Signals & Predictions
  // ==========================================================================

  /**
   * Get ML signal for a symbol
   */
  async getSignal(request: SignalRequest): Promise<MLSignal> {
    const response = await this.client.post<MLSignal>('/api/v1/signals/predict', request);

    logger.debug('[MLEngineClient] Signal generated:', {
      symbol: response.data.symbol,
      direction: response.data.direction,
      confidence: response.data.confidence,
    });

    return response.data;
  }

  /**
   * Get signals for multiple symbols
   */
  async getSignalsBatch(request: BatchSignalRequest): Promise<Record<string, MLSignal>> {
    const response = await this.client.post<{ signals: Record<string, MLSignal> }>(
      '/api/v1/signals/batch',
      request
    );
    return response.data.signals;
  }

  /**
   * Get latest signal for a symbol (cached)
   */
  async getLatestSignal(symbol: string): Promise<MLSignal | null> {
    try {
      const response = await this.client.get<MLSignal>(`/api/v1/signals/latest/${symbol}`);
      return response.data;
    } catch (error) {
      if ((error as AxiosError).response?.status === 404) {
        return null;
      }
      throw error;
    }
  }

  /**
   * Get range prediction (delta high/low)
   */
  async getRangePrediction(symbol: string, timeframe: Timeframe = '15m'): Promise<RangePrediction> {
    const response = await this.client.get<RangePrediction>(
      `/api/v1/predictions/range/${symbol}`,
      { params: { timeframe } }
    );
    return response.data;
  }

  // ==========================================================================
  // AMD Analysis
  // ==========================================================================

  /**
   * Get AMD phase analysis for a symbol
   */
  async getAMDAnalysis(symbol: string, timeframe: Timeframe = '15m'): Promise<AMDAnalysis> {
    const response = await this.client.get<AMDAnalysis>(
      `/api/v1/amd/analyze/${symbol}`,
      { params: { timeframe } }
    );

    logger.debug('[MLEngineClient] AMD Analysis:', {
      symbol,
      phase: response.data.current_phase,
      confidence: response.data.phase_confidence,
    });

    return response.data;
  }

  /**
   * Get AMD analysis for multiple symbols
   */
  async getAMDAnalysisBatch(
    symbols: string[],
    timeframe: Timeframe = '15m'
  ): Promise<Record<string, AMDAnalysis>> {
    const response = await this.client.post<{ analyses: Record<string, AMDAnalysis> }>(
      '/api/v1/amd/batch',
      { symbols, timeframe }
    );
    return response.data.analyses;
  }

  // ==========================================================================
  // Backtesting
  // ==========================================================================

  /**
   * Run backtest for a symbol/strategy
   */
  async runBacktest(request: BacktestRequest): Promise<BacktestResult> {
    const response = await this.client.post<BacktestResult>('/api/v1/backtest/run', request);

    logger.info('[MLEngineClient] Backtest completed:', {
      symbol: request.symbol,
      trades: response.data.metrics.total_trades,
      winRate: response.data.metrics.win_rate,
      return: response.data.metrics.total_return_percent,
    });

    return response.data;
  }

  /**
   * Get backtest status (for long-running backtests)
   */
  async getBacktestStatus(jobId: string): Promise<{ status: string; progress: number }> {
    const response = await this.client.get<{ status: string; progress: number }>(
      `/api/v1/backtest/status/${jobId}`
    );
    return response.data;
  }

  // ==========================================================================
  // Training (Admin only)
  // ==========================================================================

  /**
   * Trigger model retraining
   */
  async triggerTraining(
    symbol: string,
    options?: { timeframe?: Timeframe; force?: boolean }
  ): Promise<{ job_id: string; estimated_time: string }> {
    const response = await this.client.post<{ job_id: string; estimated_time: string }>(
      '/api/v1/train/start',
      { symbol, ...options }
    );

    logger.info('[MLEngineClient] Training triggered:', {
      symbol,
      jobId: response.data.job_id,
    });

    return response.data;
  }

  /**
   * Get training status
   */
  async getTrainingStatus(jobId: string): Promise<{
    status: 'pending' | 'running' | 'completed' | 'failed';
    progress: number;
    metrics?: Record<string, number>;
    error?: string;
  }> {
    const response = await this.client.get(`/api/v1/train/status/${jobId}`);
    return response.data;
  }

  // ==========================================================================
  // WebSocket Connection Info
  // ==========================================================================

  /**
   * Get WebSocket URL for real-time signals
   */
  getSignalsWebSocketUrl(): string {
    const wsProtocol = this.baseUrl.startsWith('https') ? 'wss' : 'ws';
    const wsUrl = this.baseUrl.replace(/^https?/, wsProtocol);
    return `${wsUrl}/ws/signals`;
  }
}

// Export singleton instance
export const mlEngineClient = new MLEngineClient();
