/**
 * useMT4WebSocket Hook
 * Real-time WebSocket connection for MT4 account and position updates
 */

import { useState, useEffect, useCallback, useRef } from 'react';

// Types
export interface MT4AccountInfo {
  login: number;
  name: string;
  server: string;
  balance: number;
  equity: number;
  margin: number;
  freeMargin: number;
  marginLevel: number;
  leverage: number;
  currency: string;
  profit: number;
}

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;
  commission: number;
  openTime: string;
  comment?: string;
  magicNumber?: number;
}

export interface MT4Order {
  ticket: number;
  symbol: string;
  type: 'BUY_LIMIT' | 'SELL_LIMIT' | 'BUY_STOP' | 'SELL_STOP';
  lots: number;
  price: number;
  stopLoss: number | null;
  takeProfit: number | null;
  expiration?: string;
  comment?: string;
}

interface WebSocketMessage {
  type: 'account' | 'positions' | 'orders' | 'trade' | 'error' | 'connected' | 'disconnected' | 'heartbeat';
  data?: unknown;
  error?: string;
  timestamp?: string;
}

interface UseMT4WebSocketOptions {
  url?: string;
  autoConnect?: boolean;
  reconnectInterval?: number;
  maxReconnectAttempts?: number;
  heartbeatInterval?: number;
  onAccountUpdate?: (account: MT4AccountInfo) => void;
  onPositionsUpdate?: (positions: MT4Position[]) => void;
  onOrdersUpdate?: (orders: MT4Order[]) => void;
  onTradeEvent?: (event: TradeEvent) => void;
  onError?: (error: string) => void;
  onConnectionChange?: (connected: boolean) => void;
}

interface TradeEvent {
  type: 'opened' | 'closed' | 'modified' | 'sl_hit' | 'tp_hit' | 'margin_call';
  ticket: number;
  symbol: string;
  profit?: number;
  timestamp: string;
}

interface UseMT4WebSocketReturn {
  connected: boolean;
  connecting: boolean;
  account: MT4AccountInfo | null;
  positions: MT4Position[];
  orders: MT4Order[];
  error: string | null;
  lastUpdate: Date | null;
  reconnectAttempts: number;
  connect: () => void;
  disconnect: () => void;
  subscribe: (channels: string[]) => void;
  unsubscribe: (channels: string[]) => void;
}

const DEFAULT_WS_URL = 'ws://localhost:3082/mt4';
const DEFAULT_RECONNECT_INTERVAL = 3000;
const DEFAULT_MAX_RECONNECT_ATTEMPTS = 10;
const DEFAULT_HEARTBEAT_INTERVAL = 30000;

export function useMT4WebSocket(options: UseMT4WebSocketOptions = {}): UseMT4WebSocketReturn {
  const {
    url = DEFAULT_WS_URL,
    autoConnect = true,
    reconnectInterval = DEFAULT_RECONNECT_INTERVAL,
    maxReconnectAttempts = DEFAULT_MAX_RECONNECT_ATTEMPTS,
    heartbeatInterval = DEFAULT_HEARTBEAT_INTERVAL,
    onAccountUpdate,
    onPositionsUpdate,
    onOrdersUpdate,
    onTradeEvent,
    onError,
    onConnectionChange,
  } = options;

  const [connected, setConnected] = useState(false);
  const [connecting, setConnecting] = useState(false);
  const [account, setAccount] = useState<MT4AccountInfo | null>(null);
  const [positions, setPositions] = useState<MT4Position[]>([]);
  const [orders, setOrders] = useState<MT4Order[]>([]);
  const [error, setError] = useState<string | null>(null);
  const [lastUpdate, setLastUpdate] = useState<Date | null>(null);
  const [reconnectAttempts, setReconnectAttempts] = useState(0);

  const wsRef = useRef<WebSocket | null>(null);
  const reconnectTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const heartbeatTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const subscribedChannelsRef = useRef<Set<string>>(new Set(['account', 'positions', 'orders']));

  // Cleanup function
  const cleanup = useCallback(() => {
    if (reconnectTimeoutRef.current) {
      clearTimeout(reconnectTimeoutRef.current);
      reconnectTimeoutRef.current = null;
    }
    if (heartbeatTimeoutRef.current) {
      clearTimeout(heartbeatTimeoutRef.current);
      heartbeatTimeoutRef.current = null;
    }
  }, []);

  // Reset heartbeat timer
  const resetHeartbeat = useCallback(() => {
    if (heartbeatTimeoutRef.current) {
      clearTimeout(heartbeatTimeoutRef.current);
    }
    heartbeatTimeoutRef.current = setTimeout(() => {
      // Send ping if connected
      if (wsRef.current?.readyState === WebSocket.OPEN) {
        wsRef.current.send(JSON.stringify({ type: 'ping' }));
      }
      resetHeartbeat();
    }, heartbeatInterval);
  }, [heartbeatInterval]);

  // Handle incoming messages
  const handleMessage = useCallback((event: MessageEvent) => {
    try {
      const message: WebSocketMessage = JSON.parse(event.data);
      setLastUpdate(new Date());
      resetHeartbeat();

      switch (message.type) {
        case 'account':
          const accountData = message.data as MT4AccountInfo;
          setAccount(accountData);
          onAccountUpdate?.(accountData);
          break;

        case 'positions':
          const positionsData = message.data as MT4Position[];
          setPositions(positionsData);
          onPositionsUpdate?.(positionsData);
          break;

        case 'orders':
          const ordersData = message.data as MT4Order[];
          setOrders(ordersData);
          onOrdersUpdate?.(ordersData);
          break;

        case 'trade':
          const tradeEvent = message.data as TradeEvent;
          onTradeEvent?.(tradeEvent);
          break;

        case 'error':
          const errorMsg = message.error || 'Unknown WebSocket error';
          setError(errorMsg);
          onError?.(errorMsg);
          break;

        case 'connected':
          setConnected(true);
          setConnecting(false);
          setError(null);
          setReconnectAttempts(0);
          onConnectionChange?.(true);
          // Subscribe to channels
          if (wsRef.current?.readyState === WebSocket.OPEN) {
            wsRef.current.send(JSON.stringify({
              type: 'subscribe',
              channels: Array.from(subscribedChannelsRef.current),
            }));
          }
          break;

        case 'disconnected':
          setConnected(false);
          onConnectionChange?.(false);
          break;

        case 'heartbeat':
          // Server heartbeat response - connection is alive
          break;

        default:
          console.log('Unknown message type:', message.type);
      }
    } catch (err) {
      console.error('Failed to parse WebSocket message:', err);
    }
  }, [onAccountUpdate, onPositionsUpdate, onOrdersUpdate, onTradeEvent, onError, onConnectionChange, resetHeartbeat]);

  // Connect to WebSocket
  const connect = useCallback(() => {
    if (wsRef.current?.readyState === WebSocket.OPEN || connecting) {
      return;
    }

    cleanup();
    setConnecting(true);
    setError(null);

    try {
      const ws = new WebSocket(url);

      ws.onopen = () => {
        console.log('MT4 WebSocket connected');
        resetHeartbeat();
      };

      ws.onmessage = handleMessage;

      ws.onerror = (event) => {
        console.error('MT4 WebSocket error:', event);
        setError('WebSocket connection error');
        onError?.('WebSocket connection error');
      };

      ws.onclose = (event) => {
        console.log('MT4 WebSocket closed:', event.code, event.reason);
        setConnected(false);
        setConnecting(false);
        onConnectionChange?.(false);
        cleanup();

        // Attempt reconnect if not intentionally closed
        if (event.code !== 1000 && reconnectAttempts < maxReconnectAttempts) {
          setReconnectAttempts((prev) => prev + 1);
          reconnectTimeoutRef.current = setTimeout(() => {
            console.log(`Attempting reconnect (${reconnectAttempts + 1}/${maxReconnectAttempts})...`);
            connect();
          }, reconnectInterval);
        }
      };

      wsRef.current = ws;
    } catch (err) {
      console.error('Failed to create WebSocket:', err);
      setConnecting(false);
      setError('Failed to create WebSocket connection');
      onError?.('Failed to create WebSocket connection');
    }
  }, [url, connecting, reconnectAttempts, maxReconnectAttempts, reconnectInterval, cleanup, handleMessage, resetHeartbeat, onError, onConnectionChange]);

  // Disconnect from WebSocket
  const disconnect = useCallback(() => {
    cleanup();
    if (wsRef.current) {
      wsRef.current.close(1000, 'User disconnect');
      wsRef.current = null;
    }
    setConnected(false);
    setConnecting(false);
    setReconnectAttempts(0);
  }, [cleanup]);

  // Subscribe to specific channels
  const subscribe = useCallback((channels: string[]) => {
    channels.forEach((ch) => subscribedChannelsRef.current.add(ch));
    if (wsRef.current?.readyState === WebSocket.OPEN) {
      wsRef.current.send(JSON.stringify({
        type: 'subscribe',
        channels,
      }));
    }
  }, []);

  // Unsubscribe from channels
  const unsubscribe = useCallback((channels: string[]) => {
    channels.forEach((ch) => subscribedChannelsRef.current.delete(ch));
    if (wsRef.current?.readyState === WebSocket.OPEN) {
      wsRef.current.send(JSON.stringify({
        type: 'unsubscribe',
        channels,
      }));
    }
  }, []);

  // Auto-connect on mount
  useEffect(() => {
    if (autoConnect) {
      connect();
    }
    return () => {
      disconnect();
    };
  }, [autoConnect]); // Only run on mount/unmount

  return {
    connected,
    connecting,
    account,
    positions,
    orders,
    error,
    lastUpdate,
    reconnectAttempts,
    connect,
    disconnect,
    subscribe,
    unsubscribe,
  };
}

export default useMT4WebSocket;
