/**
 * MT4 Client Service
 *
 * HTTP client wrapper for communicating with mt4-gateway.
 * This service mirrors the functionality of mt4_bridge_client.py
 * but is written in TypeScript for the MCP Server.
 */

import axios, { AxiosInstance, AxiosError } from 'axios';

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

export interface MT4AccountInfo {
  balance: number;
  equity: number;
  margin: number;
  freeMargin: number;
  marginLevel: number | null;
  profit: number;
  currency: string;
  leverage: number;
  name: string;
  server: string;
  company: string;
}

export interface MT4Position {
  ticket: number;
  symbol: string;
  type: 'buy' | 'sell';
  lots: number;
  openPrice: number;
  currentPrice: number;
  stopLoss: number | null;
  takeProfit: number | null;
  profit: number;
  swap: number;
  openTime: string;
  magic: number;
  comment: string;
}

export interface MT4Tick {
  symbol: string;
  bid: number;
  ask: number;
  timestamp: string;
  spread: number;
}

export interface TradeResult {
  success: boolean;
  ticket?: number;
  message: string;
  errorCode?: number;
}

export interface TradeRequest {
  action: 'buy' | 'sell';
  symbol: string;
  lots: number;
  stopLoss?: number;
  takeProfit?: number;
  price?: number;
  slippage?: number;
  magic?: number;
  comment?: string;
}

export interface ClosePositionRequest {
  ticket: number;
  lots?: number;
  slippage?: number;
}

export interface ModifyPositionRequest {
  ticket: number;
  stopLoss?: number;
  takeProfit?: number;
}

export interface MT4ClientConfig {
  host: string;
  port: number;
  authToken: string;
  timeout?: number;
}

// ==========================================
// MT4 Client Class
// ==========================================

export class MT4Client {
  private client: AxiosInstance;
  private baseUrl: string;

  constructor(config: MT4ClientConfig) {
    this.baseUrl = `http://${config.host}:${config.port}`;

    this.client = axios.create({
      baseURL: this.baseUrl,
      timeout: config.timeout || 10000,
      headers: {
        'Authorization': `Bearer ${config.authToken}`,
        'Content-Type': 'application/json',
      },
    });
  }

  /**
   * Check if the MT4 terminal is connected
   */
  async isConnected(): Promise<boolean> {
    try {
      const response = await this.client.get('/status');
      return response.data?.connected ?? false;
    } catch {
      return false;
    }
  }

  /**
   * Get MT4 account information
   */
  async getAccountInfo(): Promise<MT4AccountInfo> {
    try {
      const response = await this.client.get('/account');
      const data = response.data;

      return {
        balance: data.balance ?? 0,
        equity: data.equity ?? 0,
        margin: data.margin ?? 0,
        freeMargin: data.freeMargin ?? 0,
        marginLevel: data.marginLevel ?? null,
        profit: data.profit ?? 0,
        currency: data.currency ?? 'USD',
        leverage: data.leverage ?? 100,
        name: data.name ?? '',
        server: data.server ?? '',
        company: data.company ?? '',
      };
    } catch (error) {
      throw this.handleError(error, 'Failed to get account info');
    }
  }

  /**
   * Get current tick (quote) for a symbol
   */
  async getTick(symbol: string): Promise<MT4Tick> {
    try {
      const response = await this.client.get(`/tick/${symbol}`);
      const data = response.data;

      const bid = data.bid ?? 0;
      const ask = data.ask ?? 0;

      return {
        symbol,
        bid,
        ask,
        timestamp: data.time ?? new Date().toISOString(),
        spread: Math.round((ask - bid) * 100000) / 100000,
      };
    } catch (error) {
      throw this.handleError(error, `Failed to get tick for ${symbol}`);
    }
  }

  /**
   * Get all open positions
   */
  async getPositions(): Promise<MT4Position[]> {
    try {
      const response = await this.client.get('/positions');
      const data = response.data;

      if (!Array.isArray(data)) {
        return [];
      }

      return data.map((p: Record<string, unknown>) => ({
        ticket: (p.ticket as number) ?? 0,
        symbol: (p.symbol as string) ?? '',
        type: (p.type as 'buy' | 'sell') ?? 'buy',
        lots: (p.lots as number) ?? 0,
        openPrice: (p.openPrice as number) ?? 0,
        currentPrice: (p.currentPrice as number) ?? 0,
        stopLoss: (p.stopLoss as number | null) ?? null,
        takeProfit: (p.takeProfit as number | null) ?? null,
        profit: (p.profit as number) ?? 0,
        swap: (p.swap as number) ?? 0,
        openTime: (p.openTime as string) ?? new Date().toISOString(),
        magic: (p.magic as number) ?? 0,
        comment: (p.comment as string) ?? '',
      }));
    } catch (error) {
      throw this.handleError(error, 'Failed to get positions');
    }
  }

  /**
   * Get a specific position by ticket
   */
  async getPosition(ticket: number): Promise<MT4Position | null> {
    const positions = await this.getPositions();
    return positions.find(p => p.ticket === ticket) ?? null;
  }

  /**
   * Execute a trade (buy or sell)
   */
  async executeTrade(request: TradeRequest): Promise<TradeResult> {
    try {
      const payload: Record<string, unknown> = {
        action: request.action,
        symbol: request.symbol,
        lots: request.lots,
        slippage: request.slippage ?? 3,
        magic: request.magic ?? 12345,
        comment: request.comment ?? 'MCP-MT4',
      };

      if (request.stopLoss !== undefined) {
        payload.stopLoss = request.stopLoss;
      }
      if (request.takeProfit !== undefined) {
        payload.takeProfit = request.takeProfit;
      }
      if (request.price !== undefined) {
        payload.price = request.price;
      }

      const response = await this.client.post('/trade', payload);
      const data = response.data;

      return {
        success: data.success ?? false,
        ticket: data.ticket,
        message: data.message ?? '',
        errorCode: data.errorCode,
      };
    } catch (error) {
      return {
        success: false,
        message: this.getErrorMessage(error),
      };
    }
  }

  /**
   * Close a position
   */
  async closePosition(request: ClosePositionRequest): Promise<TradeResult> {
    try {
      const payload: Record<string, unknown> = {
        action: 'close',
        ticket: request.ticket,
        slippage: request.slippage ?? 3,
      };

      if (request.lots !== undefined) {
        payload.lots = request.lots;
      }

      const response = await this.client.post('/trade', payload);
      const data = response.data;

      return {
        success: data.success ?? false,
        ticket: request.ticket,
        message: data.message ?? '',
        errorCode: data.errorCode,
      };
    } catch (error) {
      return {
        success: false,
        ticket: request.ticket,
        message: this.getErrorMessage(error),
      };
    }
  }

  /**
   * Modify a position (SL/TP)
   */
  async modifyPosition(request: ModifyPositionRequest): Promise<TradeResult> {
    try {
      const payload: Record<string, unknown> = {
        action: 'modify',
        ticket: request.ticket,
      };

      if (request.stopLoss !== undefined) {
        payload.stopLoss = request.stopLoss;
      }
      if (request.takeProfit !== undefined) {
        payload.takeProfit = request.takeProfit;
      }

      const response = await this.client.post('/trade', payload);
      const data = response.data;

      return {
        success: data.success ?? false,
        ticket: request.ticket,
        message: data.message ?? '',
        errorCode: data.errorCode,
      };
    } catch (error) {
      return {
        success: false,
        ticket: request.ticket,
        message: this.getErrorMessage(error),
      };
    }
  }

  /**
   * Handle axios errors and convert to meaningful messages
   */
  private handleError(error: unknown, context: string): Error {
    const message = this.getErrorMessage(error);
    return new Error(`${context}: ${message}`);
  }

  /**
   * Extract error message from various error types
   */
  private getErrorMessage(error: unknown): string {
    if (axios.isAxiosError(error)) {
      const axiosError = error as AxiosError;
      if (axiosError.response) {
        return `HTTP ${axiosError.response.status}: ${JSON.stringify(axiosError.response.data)}`;
      }
      if (axiosError.code === 'ECONNREFUSED') {
        return 'Connection refused - MT4 Gateway is not running';
      }
      if (axiosError.code === 'ETIMEDOUT') {
        return 'Connection timeout - MT4 Gateway is not responding';
      }
      return axiosError.message;
    }
    if (error instanceof Error) {
      return error.message;
    }
    return 'Unknown error';
  }
}

// ==========================================
// Singleton Instance
// ==========================================

let clientInstance: MT4Client | null = null;

export function getMT4Client(): MT4Client {
  if (!clientInstance) {
    const config: MT4ClientConfig = {
      host: process.env.MT4_GATEWAY_HOST || 'localhost',
      port: parseInt(process.env.MT4_GATEWAY_PORT || '8081', 10),
      authToken: process.env.MT4_GATEWAY_AUTH_TOKEN || 'secret',
      timeout: parseInt(process.env.REQUEST_TIMEOUT || '10000', 10),
    };
    clientInstance = new MT4Client(config);
  }
  return clientInstance;
}

export function resetMT4Client(): void {
  clientInstance = null;
}
