import { createContext, useState, useEffect, useCallback, useRef } from 'react';
import { io } from 'socket.io-client';

export const SocketContext = createContext();

export const SocketProvider = ({ children }) => {
  const [socket, setSocket] = useState(null);
  const [connected, setConnected] = useState(false);
  const connectAttempts = useRef(0);
  const maxAttempts = 3;
  const pendingEventHandlers = useRef({});
  const isConnecting = useRef(false);

  const connect = useCallback(() => {
    if (socket || isConnecting.current) return;
    
    isConnecting.current = true;
    console.log("Connecting to socket server...");

    const serverUrl = process.env.NODE_ENV === 'production' 
      ? window.location.origin 
      : 'http://localhost:5287';
      
    try {
      const newSocket = io(serverUrl, {
        reconnectionAttempts: 3,
        timeout: 10000,
        reconnectionDelay: 1000,
        autoConnect: true
      });
      
      newSocket.on('connect', () => {
        console.log('Socket connected with ID:', newSocket.id);
        setConnected(true);
        connectAttempts.current = 0;
        isConnecting.current = false;

        Object.entries(pendingEventHandlers.current).forEach(([event, handlers]) => {
          handlers.forEach(handler => {
            console.log(`Registering pending handler for event: ${event}`);
            newSocket.on(event, handler);
          });
        });

        localStorage.setItem('playerId', newSocket.id);
      });
      
      newSocket.on('disconnect', (reason) => {
        console.log('Socket disconnected:', reason);
        setConnected(false);
      });
      
      newSocket.on('connect_error', (error) => {
        console.error('Connection error:', error);
        connectAttempts.current += 1;
        isConnecting.current = false;
        
        if (connectAttempts.current >= maxAttempts) {
          console.error('Max connection attempts reached, giving up');
          newSocket.disconnect();
        }
      });
      
      setSocket(newSocket);
    } catch (err) {
      console.error('Error creating socket connection:', err);
      isConnecting.current = false;
    }
  }, [socket]);

  useEffect(() => {
    if (!socket && !isConnecting.current) {
      connect();
    }
    
    return () => {
      pendingEventHandlers.current = {};
    };
  }, [connect, socket]);

  const disconnect = useCallback(() => {
    if (socket) {
      socket.disconnect();
      setSocket(null);
      setConnected(false);
      console.log('Socket manually disconnected');
      pendingEventHandlers.current = {};
    }
  }, [socket]);

  const send = useCallback((event, data) => {
    if (!socket) {
      console.warn(`Cannot send event "${event}": socket not connected. Auto-connecting...`);
      connect();
      return false;
    }
    
    if (!connected) {
      console.warn(`Socket exists but not connected when sending "${event}". Waiting for connection...`);
      return false;
    }
    
    try {
      socket.emit(event, data);
      return true;
    } catch (err) {
      console.error(`Error sending event "${event}":`, err);
      return false;
    }
  }, [socket, connected, connect]);

  const on = useCallback((event, callback) => {
    if (!socket) {
      console.log(`Deferring registration for event "${event}" until socket is ready`);
      
      if (!pendingEventHandlers.current[event]) {
        pendingEventHandlers.current[event] = [];
      }
      
      pendingEventHandlers.current[event].push(callback);

      if (!isConnecting.current) {
        connect();
      }

      return () => {
        if (pendingEventHandlers.current[event]) {
          const index = pendingEventHandlers.current[event].indexOf(callback);
          if (index > -1) {
            pendingEventHandlers.current[event].splice(index, 1);
          }
        }
      };
    }

    socket.on(event, callback);
    
    return () => {
      if (socket) {
        socket.off(event, callback);
      }
    };
  }, [socket, connect]);

  useEffect(() => {
    return () => {
      if (socket) {
        socket.disconnect();
      }
    };
  }, [socket]);

  const contextValue = {
    socket,
    connected, 
    connect, 
    disconnect, 
    send, 
    on,
    isConnecting: isConnecting.current
  };
  
  return (
    <SocketContext.Provider value={contextValue}>
      {children}
    </SocketContext.Provider>
  );
};
