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

const ChatConnectionContext = createContext();

export const ChatConnectionProvider = ({ children }) => {
  const { data: session, status } = useSession(); // Ajouter le hook useSession
  const [isConnected, setIsConnected] = useState(false);
  const [connectionStatus, setConnectionStatus] = useState('disconnected'); // 'disconnected', 'connecting', 'connected', 'error'
  const [userPresences, setUserPresences] = useState({}); // Nouvel état pour les présences
  const websocketRef = useRef(null);
  const reconnectTimeoutRef = useRef(null);
  const [reconnectAttempts, setReconnectAttempts] = useState(0);
  const [currentUserId, setCurrentUserId] = useState(null);
  const maxReconnectAttempts = 5;
  const isConnectedRef = useRef(false);
  const [totalUnreadCount, setTotalUnreadCount] = useState(0);

  // Système de callbacks pour les messages
  const messageCallbacksRef = useRef(new Set());

  // Fonctions pour gérer les callbacks de messages
  const addMessageCallback = useCallback((callback) => {
    messageCallbacksRef.current.add(callback);
    return () => {
      messageCallbacksRef.current.delete(callback);
    };
  }, []);

  const notifyMessageCallbacks = useCallback((data) => {
    messageCallbacksRef.current.forEach((callback) => {
      try {
        callback(data);
      } catch (error) {
        logger.error('ChatConnection: Error in message callback', error);
      }
    });
  }, []);

  // Gestion des présences utilisateur
  const handlePresenceUpdate = useCallback((data) => {
    const { user_id, status } = data;
    setUserPresences((prev) => ({
      ...prev,
      [user_id]: { status },
    }));
  }, []);

  // Configuration WebSocket
  const getWebSocketUrl = (userId) => {
    if (!userId) {
      logger.warn('ChatConnection: No user ID provided for WebSocket URL');
      return null;
    }

    const token = session?.user?.token;

    if (!token) {
      logger.warn(
        'ChatConnection: No access token found for WebSocket connection'
      );
      return null;
    }

    const baseUrl = WS_CHAT_URL(userId);
    return `${baseUrl}?token=${encodeURIComponent(token)}`;
  };

  // Connexion WebSocket
  const connectToChat = async (userId = null) => {
    const userIdToUse = userId || currentUserId;

    // Vérifier que la session est chargée
    if (status === 'loading') {
      setConnectionStatus('connecting');
      return;
    }

    if (status === 'unauthenticated' || !session) {
      logger.warn('ChatConnection: User not authenticated');
      setConnectionStatus('error');
      return;
    }

    if (!userIdToUse) {
      logger.warn('ChatConnection: Cannot connect without user ID');
      setConnectionStatus('error');
      return;
    }

    if (websocketRef.current?.readyState === WebSocket.OPEN) {
      return;
    }

    setCurrentUserId(userIdToUse);
    setConnectionStatus('connecting');

    try {
      const wsUrl = getWebSocketUrl(userIdToUse);

      if (!wsUrl) {
        throw new Error(
          'Cannot generate WebSocket URL - missing token or user ID'
        );
      }

      websocketRef.current = new WebSocket(wsUrl);

      websocketRef.current.onopen = () => {
        logger.info(
          'ChatConnection: Connected successfully for user:',
          userIdToUse
        );
        isConnectedRef.current = true;
        setIsConnected(true);
        setConnectionStatus('connected');
        setReconnectAttempts(0);
        fetchInitialUnreadCount();
      };

      websocketRef.current.onclose = (event) => {
        isConnectedRef.current = false;
        setIsConnected(false);
        setConnectionStatus('disconnected');

        // Tentative de reconnexion automatique
        if (reconnectAttempts < maxReconnectAttempts && !event.wasClean) {
          const timeout = Math.min(
            1000 * Math.pow(2, reconnectAttempts),
            30000
          );

          reconnectTimeoutRef.current = setTimeout(() => {
            setReconnectAttempts((prev) => prev + 1);
            connectToChat();
          }, timeout);
        }
      };

      websocketRef.current.onerror = (error) => {
        logger.error('ChatConnection: WebSocket error', error);
        setConnectionStatus('error');
        isConnectedRef.current = false;
        setIsConnected(false);
      };

      websocketRef.current.onmessage = (event) => {
        try {
          const data = JSON.parse(event.data);

          // Gérer les messages de présence
          if (data.type === 'presence_update') {
            handlePresenceUpdate(data);
          }

          // Incrémenter le compteur de messages non lus si c'est un message entrant
          if (data.type === 'new_message' && data.message) {
            const senderId = String(
              data.message.sender?.id ?? data.message.sender_id ?? ''
            );
            const currentId = String(session?.user?.user_id ?? '');
            if (senderId && currentId && senderId !== currentId) {
              setTotalUnreadCount((prev) => prev + 1);
            }
          }

          // Notifier tous les callbacks enregistrés
          notifyMessageCallbacks(data);
        } catch (error) {
          logger.error('ChatConnection: Error parsing message', error);
        }
      };
    } catch (error) {
      logger.error('ChatConnection: Error creating WebSocket', error);
      setConnectionStatus('error');
      isConnectedRef.current = false;
      setIsConnected(false);
    }
  };

  // Déconnexion WebSocket
  const disconnectFromChat = () => {
    if (reconnectTimeoutRef.current) {
      clearTimeout(reconnectTimeoutRef.current);
      reconnectTimeoutRef.current = null;
    }

    if (websocketRef.current) {
      websocketRef.current.close(1000, 'User disconnected');
      websocketRef.current = null;
    }

    isConnectedRef.current = false;
    setIsConnected(false);
    setConnectionStatus('disconnected');
    setReconnectAttempts(0);
    logger.info('ChatConnection: Disconnected by user');
  };

  // Envoi de message
  const sendMessage = (message) => {
    if (websocketRef.current?.readyState === WebSocket.OPEN) {
      const messageStr = JSON.stringify(message);
      websocketRef.current.send(messageStr);
      return true;
    } else {
      logger.warn('ChatConnection: Cannot send message - not connected');
      return false;
    }
  };

  // Obtenir la référence WebSocket pour les composants qui en ont besoin
  const getWebSocket = () => websocketRef.current;

  // Réinitialiser le compteur de messages non lus (ex: quand on ouvre la messagerie)
  const resetUnreadCount = useCallback(() => {
    setTotalUnreadCount(0);
  }, []);

  // Récupérer le total initial des messages non lus depuis l'API
  const fetchInitialUnreadCount = useCallback(async () => {
    const token = session?.user?.token;
    if (!token) return;
    try {
      const resp = await fetch(BE_GESTIONMESSAGERIE_CONVERSATIONS_URL, {
        credentials: 'include',
        headers: { Authorization: `Bearer ${token}` },
      });
      if (!resp.ok) return;
      const data = await resp.json();
      const conversations = Array.isArray(data) ? data : (data.results ?? []);
      const total = conversations.reduce(
        (sum, conv) => sum + (conv.unread_count || 0),
        0
      );
      setTotalUnreadCount(total);
    } catch (e) {
      logger.warn(
        'ChatConnection: impossible de récupérer le compteur non lu initial',
        e
      );
    }
  }, [session?.user?.token]);

  // Effet pour la gestion de la session et connexion automatique
  useEffect(() => {
    if (
      status === 'authenticated' &&
      session?.user?.user_id &&
      !isConnectedRef.current
    ) {
      connectToChat(session.user.user_id);
    }

    if (status === 'unauthenticated' && isConnectedRef.current) {
      disconnectFromChat();
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [status, session?.user?.user_id]);

  // Nettoyage à la destruction du composant
  useEffect(() => {
    return () => {
      if (reconnectTimeoutRef.current) {
        clearTimeout(reconnectTimeoutRef.current);
      }
      if (websocketRef.current) {
        websocketRef.current.close();
      }
    };
  }, []);

  const value = {
    isConnected,
    connectionStatus,
    userPresences,
    connectToChat,
    disconnectFromChat,
    sendMessage,
    getWebSocket,
    reconnectAttempts,
    maxReconnectAttempts,
    addMessageCallback,
    totalUnreadCount,
    resetUnreadCount,
  };

  return (
    <ChatConnectionContext.Provider value={value}>
      {children}
    </ChatConnectionContext.Provider>
  );
};

export const useChatConnection = () => {
  const context = useContext(ChatConnectionContext);
  if (!context) {
    throw new Error(
      'useChatConnection must be used within a ChatConnectionProvider'
    );
  }
  return context;
};

export default ChatConnectionContext;
