/**
 * Trading Platform Client Principal
 *
 * Cliente unificado para todos los servicios de la plataforma
 */

import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
import { AuthClient } from './auth';
import { TradingClient } from './trading';
import { MLClient } from './ml';
import { ApiResponse, AuthTokens } from './types';

export interface Trading PlatformConfig {
  baseUrl: string;
  mlEngineUrl?: string;
  llmAgentUrl?: string;
  tradingAgentsUrl?: string;
  wsUrl?: string;
  timeout?: number;
  retryAttempts?: number;
  retryDelay?: number;
}

const DEFAULT_CONFIG: Partial<Trading PlatformConfig> = {
  timeout: 30000,
  retryAttempts: 3,
  retryDelay: 1000,
};

export class Trading PlatformClient {
  private config: Trading PlatformConfig;
  private httpClient: AxiosInstance;
  private tokens: AuthTokens | null = null;

  // Sub-clients
  public auth: AuthClient;
  public trading: TradingClient;
  public ml: MLClient;

  constructor(config: Trading PlatformConfig) {
    this.config = { ...DEFAULT_CONFIG, ...config };

    // Create axios instance with interceptors
    this.httpClient = this.createHttpClient();

    // Initialize sub-clients
    this.auth = new AuthClient(this.httpClient, this.config);
    this.trading = new TradingClient(this.httpClient, this.config);
    this.ml = new MLClient(this.httpClient, this.config);

    // Setup auth callback
    this.auth.onTokensChanged = (tokens) => {
      this.tokens = tokens;
    };
  }

  private createHttpClient(): AxiosInstance {
    const instance = axios.create({
      baseURL: this.config.baseUrl,
      timeout: this.config.timeout,
      headers: {
        'Content-Type': 'application/json',
      },
    });

    // Request interceptor - add auth token
    instance.interceptors.request.use(
      (config) => {
        if (this.tokens?.accessToken) {
          config.headers.Authorization = `Bearer ${this.tokens.accessToken}`;
        }
        return config;
      },
      (error) => Promise.reject(error)
    );

    // Response interceptor - handle errors and refresh token
    instance.interceptors.response.use(
      (response) => response,
      async (error) => {
        const originalRequest = error.config;

        // If 401 and we have refresh token, try to refresh
        if (
          error.response?.status === 401 &&
          this.tokens?.refreshToken &&
          !originalRequest._retry
        ) {
          originalRequest._retry = true;

          try {
            const newTokens = await this.auth.refreshToken(
              this.tokens.refreshToken
            );
            this.tokens = newTokens;
            originalRequest.headers.Authorization = `Bearer ${newTokens.accessToken}`;
            return instance(originalRequest);
          } catch (refreshError) {
            // Refresh failed, logout
            this.tokens = null;
            this.auth.onTokensChanged?.(null);
            throw refreshError;
          }
        }

        return Promise.reject(this.normalizeError(error));
      }
    );

    return instance;
  }

  private normalizeError(error: unknown): Error {
    if (axios.isAxiosError(error)) {
      const message =
        error.response?.data?.error?.message ||
        error.response?.data?.message ||
        error.message;
      const code = error.response?.data?.error?.code || 'UNKNOWN_ERROR';
      const normalizedError = new Error(message);
      (normalizedError as any).code = code;
      (normalizedError as any).status = error.response?.status;
      return normalizedError;
    }
    return error instanceof Error ? error : new Error(String(error));
  }

  /**
   * Set authentication tokens manually
   */
  setTokens(tokens: AuthTokens | null): void {
    this.tokens = tokens;
  }

  /**
   * Get current authentication tokens
   */
  getTokens(): AuthTokens | null {
    return this.tokens;
  }

  /**
   * Check if client is authenticated
   */
  isAuthenticated(): boolean {
    return !!this.tokens?.accessToken;
  }

  /**
   * Make a raw request (for custom endpoints)
   */
  async request<T>(config: AxiosRequestConfig): Promise<ApiResponse<T>> {
    const response = await this.httpClient.request<ApiResponse<T>>(config);
    return response.data;
  }

  /**
   * Health check
   */
  async healthCheck(): Promise<{ status: string; services: Record<string, boolean> }> {
    const response = await this.httpClient.get('/health');
    return response.data;
  }
}

// Factory function for convenience
export function createTrading PlatformClient(config: Trading PlatformConfig): Trading PlatformClient {
  return new Trading PlatformClient(config);
}
