import { useState, useEffect, useRef, useCallback } from 'react';
import { useSession } from 'next-auth/react';
import { WS_CHAT_URL } from '@/utils/Url';
import logger from '@/utils/logger';

const useWebSocket = (userId, onMessage, onConnectionChange) => {
  const [isConnected, setIsConnected] = useState(false);
  const [connectionStatus, setConnectionStatus] = useState('disconnected');
  const wsRef = useRef(null);
  const reconnectTimeoutRef = useRef(null);
  const reconnectAttemptsRef = useRef(0);
  const isConnectingRef = useRef(false); // Empêcher les connexions multiples
  const maxReconnectAttempts = 5;

  // Récupération du token JWT
  const { data: session } = useSession();
  const authToken = session?.user?.token;

  // Références stables pour les callbacks
  const onMessageRef = useRef(onMessage);
  const onConnectionChangeRef = useRef(onConnectionChange);

  useEffect(() => {
    onMessageRef.current = onMessage;
  }, [onMessage]);

  useEffect(() => {
    onConnectionChangeRef.current = onConnectionChange;
  }, [onConnectionChange]);

  const connect = useCallback(() => {
    if (!userId || !authToken) {
      logger.warn('WebSocket: userId ou token manquant');
      return;
    }

    // Empêcher les connexions multiples simultanées
    if (
      isConnectingRef.current ||
      (wsRef.current && wsRef.current.readyState === WebSocket.CONNECTING)
    ) {
      logger.debug('WebSocket: connexion déjà en cours');
      return;
    }

    // Fermer la connexion existante si elle existe
    if (wsRef.current) {
      wsRef.current.close();
      wsRef.current = null;
    }

    isConnectingRef.current = true;

    try {
      // Ajouter le token à l'URL du WebSocket
      const wsUrl = new URL(WS_CHAT_URL(userId));
      wsUrl.searchParams.append('token', authToken);

      wsRef.current = new WebSocket(wsUrl.toString());

      wsRef.current.onopen = () => {
        logger.debug('WebSocket connecté');
        isConnectingRef.current = false;
        setIsConnected(true);
        setConnectionStatus('connected');
        reconnectAttemptsRef.current = 0;
        onConnectionChangeRef.current?.(true);
      };

      wsRef.current.onmessage = (event) => {
        try {
          const data = JSON.parse(event.data);
          onMessageRef.current?.(data);
        } catch (error) {
          logger.error('Erreur lors du parsing du message WebSocket:', error);
        }
      };

      wsRef.current.onclose = (event) => {
        logger.debug('WebSocket fermé:', event.code, event.reason);
        isConnectingRef.current = false;
        setIsConnected(false);
        setConnectionStatus('disconnected');
        onConnectionChangeRef.current?.(false);

        // Tentative de reconnexion automatique seulement si la fermeture n'est pas intentionnelle
        if (
          event.code !== 1000 &&
          reconnectAttemptsRef.current < maxReconnectAttempts
        ) {
          reconnectAttemptsRef.current++;
          setConnectionStatus('reconnecting');

          const delay = Math.min(
            1000 * Math.pow(2, reconnectAttemptsRef.current),
            30000
          );
          reconnectTimeoutRef.current = setTimeout(() => {
            logger.debug(
              `Tentative de reconnexion ${reconnectAttemptsRef.current}/${maxReconnectAttempts}`
            );
            connect();
          }, delay);
        } else {
          setConnectionStatus('failed');
        }
      };

      wsRef.current.onerror = (error) => {
        logger.error('Erreur WebSocket:', error);
        isConnectingRef.current = false;
        setConnectionStatus('error');
      };
    } catch (error) {
      logger.error('Erreur lors de la création du WebSocket:', error);
      isConnectingRef.current = false;
      setConnectionStatus('error');
    }
  }, [userId, authToken]);

  const disconnect = useCallback(() => {
    if (reconnectTimeoutRef.current) {
      clearTimeout(reconnectTimeoutRef.current);
      reconnectTimeoutRef.current = null;
    }

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

    setIsConnected(false);
    setConnectionStatus('disconnected');
  }, []);

  const sendMessage = useCallback(
    (message) => {
      if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
        // Ajouter le token à chaque message
        const messageWithAuth = {
          ...message,
          token: authToken,
        };
        wsRef.current.send(JSON.stringify(messageWithAuth));
        return true;
      } else {
        logger.warn("WebSocket non connecté, impossible d'envoyer le message");
        return false;
      }
    },
    [authToken]
  );

  const sendTypingStart = useCallback(
    (conversationId) => {
      sendMessage({
        type: 'typing_start',
        conversation_id: conversationId,
      });
    },
    [sendMessage]
  );

  const sendTypingStop = useCallback(
    (conversationId) => {
      sendMessage({
        type: 'typing_stop',
        conversation_id: conversationId,
      });
    },
    [sendMessage]
  );

  const markAsRead = useCallback(
    (conversationId) => {
      sendMessage({
        type: 'mark_as_read',
        conversation_id: conversationId,
      });
    },
    [sendMessage]
  );

  const joinConversation = useCallback(
    (conversationId) => {
      sendMessage({
        type: 'join_conversation',
        conversation_id: conversationId,
      });
    },
    [sendMessage]
  );

  const leaveConversation = useCallback(
    (conversationId) => {
      sendMessage({
        type: 'leave_conversation',
        conversation_id: conversationId,
      });
    },
    [sendMessage]
  );

  const sendChatMessage = useCallback(
    (conversationId, content, attachment = null) => {
      const messageData = {
        type: 'send_message',
        conversation_id: conversationId,
        content: content,
        message_type: attachment ? 'file' : 'text',
      };

      // Ajouter les informations du fichier si présent
      if (attachment) {
        messageData.attachment = attachment;
      }

      return sendMessage(messageData);
    },
    [sendMessage]
  );

  useEffect(() => {
    // Se connecter seulement si on a un userId et un token
    if (userId && authToken) {
      connect();
    }

    return () => {
      disconnect();
    };
  }, [userId, authToken]); // Retirer connect et disconnect des dépendances

  return {
    isConnected,
    connectionStatus,
    sendMessage,
    sendTypingStart,
    sendTypingStop,
    markAsRead,
    joinConversation,
    leaveConversation,
    sendChatMessage,
    reconnect: connect,
    disconnect,
  };
};

export default useWebSocket;
