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

interface WebSocketMessage {
  type: string;
  data?: any;
  message?: string;
  timestamp?: string;
  [key: string]: any;
}

interface UseGameWebSocketProps {
  token: string | null;
  onMessage: (message: WebSocketMessage) => void;
  enabled?: boolean;
}

interface UseGameWebSocketReturn {
  isConnected: boolean;
  sendMessage: (message: any) => void;
  reconnect: () => void;
}

/**
 * Custom hook for managing WebSocket connection to the game server.
 * Provides automatic reconnection, heartbeat, and message handling.
 */
export const useGameWebSocket = ({
  token,
  onMessage,
  enabled = true
}: UseGameWebSocketProps): UseGameWebSocketReturn => {
  const [isConnected, setIsConnected] = useState(false);
  const wsRef = useRef<WebSocket | null>(null);
  const reconnectTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const heartbeatIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
  const reconnectAttemptsRef = useRef(0);
  const maxReconnectAttempts = 5;
  const reconnectDelay = 3000; // 3 seconds

  // Get WebSocket URL based on current environment
  const getWebSocketUrl = useCallback(() => {
    const API_BASE = import.meta.env.VITE_API_URL || (
      import.meta.env.PROD
        ? 'https://api-staging.echoesoftheash.com'
        : 'http://localhost:8000'
    );

    // Remove /api suffix if present and convert http(s) to ws(s)
    const wsBase = API_BASE.replace(/\/api$/, '').replace(/^http/, 'ws');

    return `${wsBase}/ws/game/${token}`;
  }, [token]);

  // Send heartbeat to keep connection alive
  const sendHeartbeat = useCallback(() => {
    if (wsRef.current?.readyState === WebSocket.OPEN) {
      wsRef.current.send(JSON.stringify({ type: 'heartbeat' }));
    }
  }, []);

  // Connect to WebSocket
  const connect = useCallback(() => {
    if (!token || !enabled) return;

    try {
      const wsUrl = getWebSocketUrl();
      console.log('🔌 Connecting to WebSocket:', wsUrl);

      const ws = new WebSocket(wsUrl);
      wsRef.current = ws;

      ws.onopen = () => {
        console.log('✅ WebSocket connected');
        setIsConnected(true);
        reconnectAttemptsRef.current = 0;

        // Dispatch global event for other components to react (e.g., fetch fresh state)
        window.dispatchEvent(new Event('game-ws-connected'));

        // Start heartbeat interval (every 30 seconds)
        heartbeatIntervalRef.current = setInterval(sendHeartbeat, 30000);
      };

      ws.onmessage = (event) => {
        try {
          const message: WebSocketMessage = JSON.parse(event.data);

          // Handle heartbeat acks silently
          if (message.type === 'heartbeat_ack' || message.type === 'pong') {
            return;
          }

          if (message.id) {
            // console.log(`📩 Received msg ${message.id} type=${message.type}`);
            // Send ACK
            wsRef.current?.send(JSON.stringify({ type: "ack", reply_to: message.id }));
          }

          // Dispatch to global event bus so other components (like Game.tsx) can react
          // without needing their own WebSocket connection
          window.dispatchEvent(new CustomEvent('game-ws-message', { detail: message }));

          // Pass message to handler
          onMessage(message);
        } catch (error) {
          console.error('❌ Error parsing WebSocket message:', error);
        }
      };

      ws.onerror = (error) => {
        console.error('❌ WebSocket error:', error);
      };

      ws.onclose = () => {
        console.log('🔌 WebSocket disconnected');
        setIsConnected(false);

        // Clear heartbeat interval
        if (heartbeatIntervalRef.current) {
          clearInterval(heartbeatIntervalRef.current);
          heartbeatIntervalRef.current = null;
        }

        // Attempt reconnection if enabled and under max attempts
        if (enabled && reconnectAttemptsRef.current < maxReconnectAttempts) {
          reconnectAttemptsRef.current += 1;
          console.log(
            `🔄 Reconnecting... (attempt ${reconnectAttemptsRef.current}/${maxReconnectAttempts})`
          );

          reconnectTimeoutRef.current = setTimeout(() => {
            connect();
          }, reconnectDelay);
        } else if (reconnectAttemptsRef.current >= maxReconnectAttempts) {
          console.error('❌ Max reconnection attempts reached');
        }
      };
    } catch (error) {
      console.error('❌ Error creating WebSocket:', error);
    }
  }, [token, enabled, getWebSocketUrl, onMessage, sendHeartbeat]);

  // Disconnect from WebSocket
  const disconnect = useCallback(() => {
    // Clear reconnection timeout
    if (reconnectTimeoutRef.current) {
      clearTimeout(reconnectTimeoutRef.current);
      reconnectTimeoutRef.current = null;
    }

    // Clear heartbeat interval
    if (heartbeatIntervalRef.current) {
      clearInterval(heartbeatIntervalRef.current);
      heartbeatIntervalRef.current = null;
    }

    // Close WebSocket
    if (wsRef.current) {
      wsRef.current.close();
      wsRef.current = null;
    }

    setIsConnected(false);
  }, []);

  // Send message through WebSocket
  const sendMessage = useCallback((message: any) => {
    if (wsRef.current?.readyState === WebSocket.OPEN) {
      wsRef.current.send(JSON.stringify(message));
    } else {
      console.warn('⚠️ WebSocket not connected, cannot send message');
    }
  }, []);

  // Manual reconnect function
  const reconnect = useCallback(() => {
    disconnect();
    reconnectAttemptsRef.current = 0;
    setTimeout(connect, 500);
  }, [connect, disconnect]);

  // Effect: Connect/disconnect based on token and enabled status
  useEffect(() => {
    if (!token || !enabled) {
      return;
    }

    // Connect on mount
    connect();

    // Cleanup on unmount or when dependencies change
    return () => {
      disconnect();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [token, enabled]); // Only reconnect when token or enabled changes

  return {
    isConnected,
    sendMessage,
    reconnect
  };
};
