import { useEffect, useRef, useState, useCallback } from 'react';
import { useSession } from 'next-auth/react';

interface WebSocketMessage {
  type: string;
  [key: string]: any;
}

interface UseWebSocketOptions {
  onMessage?: (message: WebSocketMessage) => void;
  onConnect?: () => void;
  onDisconnect?: () => void;
  onError?: (error: Event) => void;
  autoConnect?: boolean;
  reconnectInterval?: number;
  maxReconnectAttempts?: number;
}

interface UseWebSocketReturn {
  isConnected: boolean;
  isConnecting: boolean;
  sendMessage: (message: WebSocketMessage) => void;
  connect: () => void;
  disconnect: () => void;
  joinRoom: (roomId: string) => void;
  leaveRoom: (roomId: string) => void;
  connectionId: string | null;
  lastMessage: WebSocketMessage | null;
}

export const useWebSocket = (options: UseWebSocketOptions = {}): UseWebSocketReturn => {
  const { data: session } = useSession();
  const {
    onMessage,
    onConnect,
    onDisconnect,
    onError,
    autoConnect = true,
    reconnectInterval = 5000,
    maxReconnectAttempts = 3
  } = options;

  const [isConnected, setIsConnected] = useState(false);
  const [isConnecting, setIsConnecting] = useState(false);
  const [connectionId, setConnectionId] = useState<string | null>(null);
  const [lastMessage, setLastMessage] = useState<WebSocketMessage | null>(null);
  
  const wsRef = useRef<WebSocket | null>(null);
  const reconnectTimeoutRef = useRef<NodeJS.Timeout | null>(null);
  const reconnectAttemptsRef = useRef(0);
  const mountedRef = useRef(true);

  const getWebSocketUrl = useCallback(() => {
    if (typeof window === 'undefined') {
      console.log('🔗 [WebSocket] Server-side, no URL');
      return '';
    }
    
    const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
    const hostname = window.location.hostname;
    const currentPort = window.location.port;
    
    console.log('🔗 [WebSocket] URL generation:', {
      protocol,
      hostname,
      currentPort,
      nodeEnv: process.env.NODE_ENV,
      locationHost: window.location.host,
      locationHref: window.location.href,
      windowLocation: window.location
    });
    
    // In development, use explicit port 3005. In production, use current port
    let port = '3005';
    if (process.env.NODE_ENV === 'production' && currentPort) {
      port = currentPort;
    } else if (process.env.NODE_ENV === 'production') {
      // In production without port, don't specify port (use default 80/443)
      const wsUrl = `${protocol}//${hostname}/api/websocket`;
      console.log('🔗 [WebSocket] Generated WebSocket URL (production):', wsUrl);
      return wsUrl;
    }
    
    const wsUrl = `${protocol}//${hostname}:${port}/api/websocket`;
    console.log('🔗 [WebSocket] Generated WebSocket URL:', wsUrl);
    return wsUrl;
  }, []);

  const sendMessage = useCallback((message: WebSocketMessage) => {
    if (!wsRef.current) {
      console.warn('🔗 [WebSocket] No connection available to send message');
      return false;
    }
    
    if (wsRef.current.readyState !== WebSocket.OPEN) {
      console.warn('🔗 [WebSocket] Connection not ready to send message, state:', wsRef.current.readyState);
      return false;
    }
    
    try {
      const messageStr = JSON.stringify(message);
      wsRef.current.send(messageStr);
      console.log('📤 [WebSocket] Message sent:', message.type);
      return true;
    } catch (error) {
      console.error('❌ [WebSocket] Failed to send message:', error);
      return false;
    }
  }, []);

  const joinRoom = useCallback((roomId: string) => {
    sendMessage({ type: 'join_room', roomId });
  }, [sendMessage]);

  const leaveRoom = useCallback((roomId: string) => {
    sendMessage({ type: 'leave_room', roomId });
  }, [sendMessage]);

  const handleConnect = useCallback(() => {
    setIsConnected(true);
    setIsConnecting(false);
    reconnectAttemptsRef.current = 0;
    onConnect?.();

    // Authenticate if session is available
    if (session?.user && (session.user as any)?.id) {
      const userId = (session.user as any).id;
      const authMessage = {
        type: 'auth',
        userId: userId,
        email: session.user.email
      };
      
      console.log('🔑 [WebSocket] Authenticating with:', {
        userId: userId,
        email: session.user.email,
        hasSession: !!session,
        sessionUser: session.user
      });
      
      setTimeout(() => {
        console.log('📤 [WebSocket] Sending auth message:', authMessage);
        const sent = sendMessage(authMessage);
        console.log('📫 [WebSocket] Auth message sent result:', sent);
      }, 100);
    } else {
      console.warn('⚠️ [WebSocket] Cannot authenticate - no session or user ID:', {
        hasSession: !!session,
        hasUser: !!session?.user,
        hasUserId: !!(session?.user as any)?.id,
        sessionUser: session?.user
      });
    }
  }, [session, onConnect, sendMessage]);

  const handleDisconnect = useCallback(() => {
    setIsConnected(false);
    setIsConnecting(false);
    setConnectionId(null);
    onDisconnect?.();
  }, [onDisconnect]);

  const handleMessage = useCallback((event: MessageEvent) => {
    console.log('🚀 [WebSocket] handleMessage called with event:', {
      data: event.data,
      type: typeof event.data,
      hasOnMessage: !!onMessage
    });
    
    try {
      const message: WebSocketMessage = JSON.parse(event.data);
      console.log('💬 [WebSocket] Parsed message:', message);
      
      // Handle system messages internally
      switch (message.type) {
        case 'connected':
          setConnectionId(message.connectionId);
          console.log('🔌 [WebSocket] Connected with ID:', message.connectionId);
          break;
        case 'auth_success':
          console.log('🔑 [WebSocket] Authentication successful:', message.userId);
          break;
        case 'auth_error':
          console.error('❌ [WebSocket] Authentication failed:', message.message);
          break;
        case 'error':
          console.error('❌ [WebSocket] Server error:', message.message);
          break;
        case 'pong':
          console.log('🏓 [WebSocket] Received pong response');
          break;
        case 'new_message':
          console.log('📨 [WebSocket] New message received:', message);
          break;
        default:
          console.log('🔍 [WebSocket] Unhandled message type:', message.type);
      }
      
      // Always pass all messages to the user's callback
      if (onMessage) {
        console.log('🚀 [WebSocket] Calling user onMessage callback with:', message);
        onMessage(message);
      } else {
        console.warn('⚠️ [WebSocket] No onMessage callback provided!');
      }
      setLastMessage(message);
    } catch (error) {
      console.error('❌ [WebSocket] Error parsing WebSocket message:', {
        error,
        rawData: event.data,
        dataType: typeof event.data
      });
    }
  }, [onMessage]);

  const handleError = useCallback((error: Event) => {
    console.error('❌ [WebSocket] Connection error:', error);
    setIsConnecting(false);
    onError?.(error);
  }, [onError]);

  const scheduleReconnect = useCallback(() => {
    if (!mountedRef.current) return;
    
    if (reconnectAttemptsRef.current < maxReconnectAttempts) {
      reconnectAttemptsRef.current++;
      console.log(`🔄 [WebSocket] Scheduling reconnect attempt ${reconnectAttemptsRef.current}/${maxReconnectAttempts} in ${reconnectInterval}ms`);
      
      reconnectTimeoutRef.current = setTimeout(() => {
        if (mountedRef.current && !isConnected && !isConnecting) {
          // Call connect directly without circular dependency
          attemptConnection();
        }
      }, reconnectInterval);
    } else {
      console.warn(`❌ [WebSocket] Max reconnection attempts (${maxReconnectAttempts}) reached`);
    }
  }, [maxReconnectAttempts, reconnectInterval, isConnected, isConnecting]);

  const attemptConnection = useCallback(() => {
    console.log('🔗 [WebSocket] attemptConnection called:', {
      mounted: mountedRef.current,
      currentState: wsRef.current?.readyState,
      isConnected,
      isConnecting
    });
    
    if (!mountedRef.current) {
      console.log('🔗 [WebSocket] Component unmounted, skipping connection');
      return;
    }
    
    if (isConnecting) {
      console.log('🔗 [WebSocket] Already attempting to connect...');
      return;
    }
    
    if (wsRef.current && wsRef.current.readyState === WebSocket.CONNECTING) {
      console.log('🔗 [WebSocket] WebSocket already connecting...');
      return; // Already connecting
    }

    if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
      console.log('🔗 [WebSocket] Already connected');
      return; // Already connected
    }

    const wsUrl = getWebSocketUrl();
    console.log('🔗 [WebSocket] Generated URL:', wsUrl);
    
    if (!wsUrl) {
      console.error('❌ [WebSocket] No URL available for connection');
      return;
    }

    console.log('🔗 [WebSocket] Attempting to connect to:', wsUrl);
    setIsConnecting(true);

    try {
      console.log('🚀 [WebSocket] Creating new WebSocket instance...');
      console.log('🔗 [WebSocket] Browser WebSocket support:', typeof WebSocket !== 'undefined');
      console.log('🔗 [WebSocket] About to create WebSocket with URL:', wsUrl);
      
      const ws = new WebSocket(wsUrl);
      wsRef.current = ws;
      
      console.log('🔗 [WebSocket] WebSocket instance created, initial readyState:', ws.readyState);
      
      console.log('🔗 [WebSocket] WebSocket object created:', {
        readyState: ws.readyState,
        url: ws.url,
        protocol: ws.protocol,
        extensions: ws.extensions
      });

      ws.onopen = () => {
        console.log('✅ [WebSocket] Connection opened successfully! ReadyState:', ws.readyState);
        console.log('🔗 [WebSocket] Connection details:', {
          url: ws.url,
          protocol: ws.protocol,
          readyState: ws.readyState,
          extensions: ws.extensions
        });
        handleConnect();
      };
      
      ws.onclose = (event) => {
        console.log('🔌 [WebSocket] Connection closed:', {
          code: event.code,
          reason: event.reason,
          wasClean: event.wasClean,
          readyState: ws.readyState
        });
        handleDisconnect();
        
        // Attempt to reconnect if the connection was not closed intentionally
        if (event.code !== 1000 && mountedRef.current) {
          console.log('🔄 [WebSocket] Scheduling reconnect...');
          scheduleReconnect();
        }
      };
      
      ws.onmessage = (event) => {
        console.log('📥 [WebSocket] Raw message received:', {
          data: event.data,
          type: typeof event.data,
          readyState: ws.readyState,
          timestamp: new Date().toISOString()
        });
        handleMessage(event);
      };
      
      ws.onerror = (error) => {
        console.error('❌ [WebSocket] Connection error occurred:', {
          error,
          readyState: ws.readyState,
          url: wsUrl,
          type: error.type,
          target: error.target
        });
        console.error('❌ [WebSocket] Error details:', error);
        setIsConnecting(false);
        handleError(error);
      };

    } catch (error) {
      console.error('❌ [WebSocket] Failed to create connection:', error);
      setIsConnecting(false);
      scheduleReconnect();
    }
  }, [getWebSocketUrl, handleConnect, handleDisconnect, handleMessage, handleError, scheduleReconnect, isConnecting, isConnected]);

  const connect = useCallback(() => {
    attemptConnection();
  }, [attemptConnection]);

  const disconnect = useCallback(() => {
    console.log('🔌 [WebSocket] Disconnect called, mounted:', mountedRef.current);
    mountedRef.current = false;
    
    if (reconnectTimeoutRef.current) {
      clearTimeout(reconnectTimeoutRef.current);
      reconnectTimeoutRef.current = null;
    }

    if (wsRef.current) {
      console.log('🔌 [WebSocket] Closing WebSocket connection');
      wsRef.current.close(1000); // Normal closure
      wsRef.current = null;
    }

    setIsConnected(false);
    setIsConnecting(false);
    setConnectionId(null);
  }, []);

  // Auto-connect effect with delay to avoid race conditions
  useEffect(() => {
    console.log('🔍 [WebSocket] Auto-connect check:', {
      autoConnect,
      hasSession: !!session,
      userEmail: session?.user?.email,
      userId: (session?.user as any)?.id,
      isConnected,
      isConnecting,
      mountedRef: mountedRef.current,
      currentWsState: wsRef.current?.readyState
    });
    
    if (autoConnect && session?.user?.email && !isConnected && !isConnecting && mountedRef.current) {
      console.log('🚀 [WebSocket] Starting connection attempt...');
      // Small delay to avoid React Strict Mode issues
      const timer = setTimeout(() => {
        if (mountedRef.current) {
          console.log('🔄 [WebSocket] Executing delayed connection attempt...');
          attemptConnection();
        }
      }, 50);
      
      return () => {
        clearTimeout(timer);
      };
    }
  }, [autoConnect, session?.user?.email, isConnected, isConnecting, attemptConnection]);

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      disconnect();
    };
  }, [disconnect]);

  // Ping to keep connection alive
  useEffect(() => {
    if (!isConnected) return;

    const pingInterval = setInterval(() => {
      if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
        sendMessage({ type: 'ping' });
      }
    }, 30000); // Ping every 30 seconds

    return () => clearInterval(pingInterval);
  }, [isConnected, sendMessage]);

  return {
    isConnected,
    isConnecting,
    sendMessage,
    connect,
    disconnect,
    joinRoom,
    leaveRoom,
    connectionId,
    lastMessage
  };
};