/**
 * LLM Agent Client
 * Client for communicating with the LLM Agent Python service (Trading Copilot)
 *
 * @see apps/llm-agent/IMPLEMENTATION_SUMMARY.md
 */

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

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

export type UserPlan = 'free' | 'pro' | 'premium';
export type UserLevel = 'beginner' | 'intermediate' | 'advanced';

export interface ChatMessage {
  role: 'user' | 'assistant' | 'system';
  content: string;
  timestamp?: string;
  tool_calls?: ToolCall[];
}

export interface ToolCall {
  id: string;
  name: string;
  arguments: Record<string, unknown>;
  result?: unknown;
}

export interface ChatRequest {
  user_id: string;
  conversation_id?: string;
  message: string;
  user_plan?: UserPlan;
  user_level?: UserLevel;
  stream?: boolean;
  context?: {
    current_symbol?: string;
    current_timeframe?: string;
    portfolio_value?: number;
  };
}

export interface ChatResponse {
  conversation_id: string;
  message: ChatMessage;
  tools_used: string[];
  tokens_used: number;
  model: string;
  processing_time_ms: number;
}

export interface AnalyzeRequest {
  user_id: string;
  symbol: string;
  timeframe?: string;
  user_plan?: UserPlan;
  include_ml_signal?: boolean;
  include_amd?: boolean;
}

export interface AnalyzeResponse {
  symbol: string;
  analysis: string;
  technical_summary: {
    trend: 'bullish' | 'bearish' | 'neutral';
    strength: number;
    key_levels: {
      support: number[];
      resistance: number[];
    };
    indicators: Record<string, unknown>;
  };
  ml_signal?: {
    direction: string;
    confidence: number;
    entry: number;
    stop_loss: number;
    take_profit: number;
  };
  amd_phase?: {
    phase: string;
    confidence: number;
    recommendations: string[];
  };
  risk_assessment: string;
  disclaimer: string;
}

export interface StrategyRequest {
  user_id: string;
  symbol: string;
  risk_tolerance: 'conservative' | 'moderate' | 'aggressive';
  capital?: number;
  timeframe?: string;
  user_plan?: UserPlan;
}

export interface StrategyResponse {
  symbol: string;
  strategy_name: string;
  description: string;
  entry_conditions: string[];
  exit_conditions: string[];
  position_sizing: {
    max_position_percent: number;
    recommended_lot_size: number;
    stop_loss_distance: number;
  };
  risk_reward_ratio: number;
  expected_win_rate: number;
  backtested_results?: {
    period: string;
    total_return: number;
    max_drawdown: number;
    sharpe_ratio: number;
  };
  warnings: string[];
  disclaimer: string;
}

export interface ExplainRequest {
  concept: string;
  level?: UserLevel;
  with_examples?: boolean;
}

export interface ExplainResponse {
  concept: string;
  explanation: string;
  examples?: string[];
  related_concepts: string[];
  resources?: { title: string; url: string }[];
}

export interface ToolInfo {
  name: string;
  description: string;
  required_plan: UserPlan;
  parameters: Record<string, { type: string; description: string; required: boolean }>;
}

export interface ConversationHistory {
  conversation_id: string;
  user_id: string;
  messages: ChatMessage[];
  created_at: string;
  last_message_at: string;
  total_messages: number;
}

export interface LLMAgentHealthResponse {
  status: 'healthy' | 'unhealthy';
  version: string;
  llm_provider: string;
  llm_model: string;
  tools_available: number;
  ollama_status: 'connected' | 'disconnected';
}

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

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

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

    this.client = axios.create({
      baseURL: this.baseUrl,
      timeout: 120000, // LLM responses can take time
      headers: {
        'Content-Type': 'application/json',
      },
    });

    // Request interceptor
    this.client.interceptors.request.use(
      (config) => {
        logger.debug('[LLMAgentClient] Request:', {
          method: config.method?.toUpperCase(),
          url: config.url,
        });
        return config;
      },
      (error) => {
        logger.error('[LLMAgentClient] 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('[LLMAgentClient] Response error:', {
          status,
          message,
          url: error.config?.url,
        });

        throw new Error(`LLM Agent API error: ${message}`);
      }
    );
  }

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

  /**
   * Check if LLM Agent service is healthy
   */
  async healthCheck(): Promise<LLMAgentHealthResponse> {
    const response = await this.client.get<LLMAgentHealthResponse>('/api/v1/health');
    return response.data;
  }

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

  /**
   * Get available tools for a user plan
   */
  async getTools(userPlan: UserPlan = 'free'): Promise<ToolInfo[]> {
    const response = await this.client.get<{ tools: ToolInfo[] }>('/api/v1/tools', {
      params: { user_plan: userPlan },
    });
    return response.data.tools;
  }

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

  // ==========================================================================
  // Chat & Conversation
  // ==========================================================================

  /**
   * Send a chat message and get response
   */
  async chat(request: ChatRequest): Promise<ChatResponse> {
    const response = await this.client.post<ChatResponse>('/api/v1/chat', {
      ...request,
      stream: false, // Non-streaming for this method
    });

    logger.debug('[LLMAgentClient] Chat response:', {
      conversationId: response.data.conversation_id,
      toolsUsed: response.data.tools_used,
      tokens: response.data.tokens_used,
    });

    return response.data;
  }

  /**
   * Get streaming chat response URL
   * Returns the URL to connect via SSE for streaming responses
   */
  getStreamingChatUrl(): string {
    return `${this.baseUrl}/api/v1/chat/stream`;
  }

  /**
   * Get conversation history
   */
  async getConversation(
    userId: string,
    conversationId: string
  ): Promise<ConversationHistory | null> {
    try {
      const response = await this.client.get<ConversationHistory>(
        `/api/v1/conversations/${userId}/${conversationId}`
      );
      return response.data;
    } catch (error) {
      if ((error as AxiosError).response?.status === 404) {
        return null;
      }
      throw error;
    }
  }

  /**
   * Get all conversations for a user
   */
  async getUserConversations(
    userId: string,
    options?: { limit?: number; offset?: number }
  ): Promise<ConversationHistory[]> {
    const response = await this.client.get<{ conversations: ConversationHistory[] }>(
      `/api/v1/conversations/${userId}`,
      { params: options }
    );
    return response.data.conversations;
  }

  /**
   * Delete a conversation
   */
  async deleteConversation(userId: string, conversationId: string): Promise<void> {
    await this.client.delete(`/api/v1/context/${userId}/${conversationId}`);

    logger.info('[LLMAgentClient] Conversation deleted:', {
      userId,
      conversationId,
    });
  }

  // ==========================================================================
  // Analysis & Strategy
  // ==========================================================================

  /**
   * Get comprehensive analysis for a symbol
   */
  async analyze(request: AnalyzeRequest): Promise<AnalyzeResponse> {
    const response = await this.client.post<AnalyzeResponse>('/api/v1/analyze', request);

    logger.debug('[LLMAgentClient] Analysis completed:', {
      symbol: request.symbol,
      trend: response.data.technical_summary.trend,
    });

    return response.data;
  }

  /**
   * Generate trading strategy for a symbol
   */
  async generateStrategy(request: StrategyRequest): Promise<StrategyResponse> {
    const response = await this.client.post<StrategyResponse>('/api/v1/strategy', request);

    logger.debug('[LLMAgentClient] Strategy generated:', {
      symbol: request.symbol,
      strategy: response.data.strategy_name,
    });

    return response.data;
  }

  // ==========================================================================
  // Education
  // ==========================================================================

  /**
   * Explain a trading concept
   */
  async explainConcept(request: ExplainRequest): Promise<ExplainResponse> {
    const response = await this.client.post<ExplainResponse>('/api/v1/explain', request);
    return response.data;
  }

  /**
   * Get available concepts that can be explained
   */
  async getAvailableConcepts(): Promise<string[]> {
    const response = await this.client.get<{ concepts: string[] }>('/api/v1/concepts');
    return response.data.concepts;
  }

  // ==========================================================================
  // Utility Methods
  // ==========================================================================

  /**
   * Format a chat request for streaming via SSE
   */
  formatStreamingRequest(request: ChatRequest): string {
    return JSON.stringify({
      ...request,
      stream: true,
    });
  }

  /**
   * Parse SSE event data
   */
  parseSSEEvent(data: string): { type: string; content: string } | null {
    try {
      return JSON.parse(data);
    } catch {
      return null;
    }
  }
}

// Export singleton instance
export const llmAgentClient = new LLMAgentClient();
