'use client';

import { useState, useEffect, useCallback, useRef } from 'react';
import { useUser } from '@/app/contexts/UserContext';
import { useSupabaseUser } from '@/app/contexts/SupabaseUserContext';
import { useTranslations } from 'next-intl';
import { supabase } from '@/lib/supabase';
import { socket } from '@/lib/socket';
import type { ChatMessage, SupabaseMessage } from '../types/supabase';
import { convertSupabaseMessageToChatMessage } from '../types/supabase';

export const useMessages = (conversationId: string | null) => {
  const { user } = useUser();
  const { supabaseUser } = useSupabaseUser();
  const t = useTranslations('chat.errors');
  const tLoading = useTranslations('chat.loading');
  const [messages, setMessages] = useState<ChatMessage[]>([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [sending, setSending] = useState(false);
  const retryCountRef = useRef(0);
  const maxRetries = 3;

  // Fonction pour marquer les messages comme lus
  const markMessagesAsRead = useCallback(async (convId: string, userId: string) => {
    try {
      // 1) Récupérer les IDs des messages entrants
      const { data: incomingMsgs } = await supabase
        .from('messages')
        .select('id')
        .eq('conversation_id', convId)
        .neq('sender_id', userId);

      const incomingIds = (incomingMsgs || []).map(m => m.id);
      if (incomingIds.length === 0) return;

      // 2) Récupérer ceux déjà lus par l'utilisateur
      const { data: readsForUser } = await supabase
        .from('message_reads')
        .select('message_id')
        .eq('user_id', userId)
        .in('message_id', incomingIds);

      const readIds = new Set((readsForUser || []).map(r => r.message_id));
      const unreadIds = incomingIds.filter(id => !readIds.has(id));

      if (unreadIds.length === 0) {
        return;
      }

      // Marquer tous comme lus
      const reads = unreadIds.map(id => ({
        message_id: id,
        user_id: userId,
        read_at: new Date().toISOString()
      }));

      await supabase
        .from('message_reads')
        .upsert(reads, { onConflict: 'message_id,user_id' });
    } catch (error) {
      console.error('Error marking messages as read:', error);
    }
  }, []);

  // 3.1 Chargement messages depuis Supabase
  const loadMessages = useCallback(async (isRetry: boolean = false) => {
    if (!conversationId || !user?.id) {
      setMessages([]);
      setLoading(false);
      setError(null);
      return;
    }

    try {
      if (!isRetry) {
        setLoading(true);
      }
      setError(null);

      if (!supabaseUser) {
        throw new Error(t('userNotAvailable'));
      }

      // Charger les messages depuis Supabase
      const { data: messagesData, error: messagesError } = await supabase
        .from('messages')
        .select('*')
        .eq('conversation_id', conversationId)
        .order('created_at', { ascending: true });

      if (messagesError) {
        // Vérifier si c'est une erreur de connexion Supabase
        if (messagesError.message.includes('fetch') || messagesError.message.includes('network')) {
          throw new Error(t('supabaseUnavailable'));
        }
        throw messagesError;
      }

      if (!messagesData || messagesData.length === 0) {
        setMessages([]);
        setLoading(false);
        return;
      }

      // Obtenir les statuts de lecture (moi)
      const messageIds = messagesData.map(m => m.id);
      const { data: reads } = await supabase
        .from('message_reads')
        .select('message_id')
        .in('message_id', messageIds)
        .eq('user_id', supabaseUser.id);

      const readMessageIds = new Set(reads?.map(r => r.message_id) || []);

      // Obtenir les statuts de lecture par le(s) destinataire(s) pour mes messages
      const { data: participants } = await supabase
        .from('conversation_participants')
        .select('user_id')
        .eq('conversation_id', conversationId);

      const otherUserIds = (participants || [])
        .map(p => p.user_id)
        .filter(uid => uid !== supabaseUser.id);

      let readByOthersIds = new Set<string>();
      if (otherUserIds.length > 0) {
        const { data: readsByOthers } = await supabase
          .from('message_reads')
          .select('message_id, user_id')
          .in('message_id', messageIds)
          .in('user_id', otherUserIds);
        readByOthersIds = new Set((readsByOthers || []).map(r => r.message_id));
      }

      // Convertir en ChatMessage
      const chatMessages = messagesData.map(message => {
        const isMine = message.sender_id === supabaseUser.id;
        const isReadByOthers = isMine && readByOthersIds.has(message.id);
        return convertSupabaseMessageToChatMessage(
          message as SupabaseMessage,
          readMessageIds.has(message.id),
          isReadByOthers
        );
      });

      setMessages(chatMessages);

      // Marquer tous les messages non lus comme lus
      await markMessagesAsRead(conversationId, supabaseUser.id);
      retryCountRef.current = 0; // Reset retry count on success
    } catch (err) {
      const errorMessage = err instanceof Error ? err.message : t('loadingMessages');
      
      // Retry automatique pour les erreurs réseau
      if (!isRetry && retryCountRef.current < maxRetries && 
          (errorMessage.includes('network') || errorMessage.includes('fetch') || errorMessage.includes(t('supabaseUnavailable')))) {
        retryCountRef.current++;
        await new Promise(resolve => setTimeout(resolve, 1000 * retryCountRef.current)); // Délai exponentiel
        // Retry en appelant la fonction directement (pas récursif via useCallback)
        const retryFn = loadMessages;
        return retryFn(true);
      }
      
      setError(errorMessage);
      console.error('Error loading messages:', err);
      retryCountRef.current = 0; // Reset on final error
    } finally {
      if (!isRetry || retryCountRef.current >= maxRetries) {
        setLoading(false);
      }
    }
  }, [conversationId, user?.id, supabaseUser?.id, markMessagesAsRead, t]);

  // 3.2 Envoi messages (flux complet : Supabase puis Socket.IO)
  const sendMessage = async (text: string) => {
    if (!text.trim() || !conversationId || !user?.id) {
      return { success: false, error: 'Invalid message payload' };
    }

    try {
      setSending(true);
      setError(null);

      if (!supabaseUser) {
        throw new Error(t('userNotAvailable'));
      }

      // Étape 1 : Insérer dans Supabase (persistance)
      const { data: message, error: messageError } = await supabase
        .from('messages')
        .insert({
          conversation_id: conversationId,
          sender_id: supabaseUser.id,
          content: text,
          message_type: 'text',
          status: 'sent'
        })
        .select()
        .single();

      if (messageError || !message) {
        // Gérer les erreurs spécifiques
        if (messageError?.message.includes('fetch') || messageError?.message.includes('network')) {
          throw new Error(t('supabaseUnavailable'));
        }
        throw messageError || new Error(t('sendingMessage'));
      }

      // Marquer comme lu par l'expéditeur
      await supabase
        .from('message_reads')
        .upsert({
          message_id: message.id,
          user_id: supabaseUser.id,
          read_at: new Date().toISOString()
        });

      // Étape 2 : Diffuser via Socket.IO
      if (socket.connected) {
        socket.emit('send-message', {
          text: message.content,
          senderId: supabaseUser.id,
          conversationId: conversationId,
          messageId: message.id,
          timestamp: message.created_at
        });
      } else {
        // Si Socket.IO non connecté, on continue quand même car le message est sauvegardé
        console.warn('Socket.IO not connected, message saved but not broadcasted');
      }

      // Ajouter le message à l'état local
      const chatMessage = convertSupabaseMessageToChatMessage(
        message as SupabaseMessage,
        true, // lu par l'expéditeur
        false // pas encore lu par le destinataire
      );
      setMessages(prev => [...prev, chatMessage]);

      return { success: true, message };
    } catch (err) {
      const errorMessage = err instanceof Error ? err.message : t('sendingMessage');
      setError(errorMessage);
      console.error('Error sending message:', err);
      return { success: false, error: errorMessage };
    } finally {
      setSending(false);
    }
  };

  // 3.3 Temps réel messages via Socket.IO
  useEffect(() => {
    if (!conversationId || !user?.id) {
      return;
    }

    // Connexion Socket.IO si pas connecté
    if (!socket.connected) {
      socket.connect();
    }

    // Rejoindre la conversation
    socket.emit('join-conversation', conversationId);

    // Écouter les nouveaux messages
    const handleNewMessage = (data: any) => {
      // Vérifier que c'est pour cette conversation
      if (data.conversationId !== conversationId) {
        return;
      }

      // Ignorer les messages envoyés par l'utilisateur connecté (déjà ajoutés optimistiquement)
      if (supabaseUser?.id && data.senderId === supabaseUser.id) {
        return;
      }

      // Vérifier les doublons
      setMessages(prev => {
        const exists = prev.some(msg => msg.id === data.messageId || (msg.content === data.text && msg.sender_id === data.senderId && Math.abs(msg.timestamp - new Date(data.timestamp || Date.now()).getTime()) < 2000));
        if (exists) return prev;

        // Créer un ChatMessage depuis les données Socket.IO
        // Si la conversation est ouverte, le message est automatiquement lu
        const messageId = data.messageId || Date.now().toString();
        const newMessage: ChatMessage = {
          id: messageId,
          conversation_id: data.conversationId,
          sender_id: data.senderId,
          content: data.text,
          text: data.text,
          message_type: 'text',
          status: 'sent',
          created_at: data.timestamp || new Date().toISOString(),
          updated_at: data.timestamp || new Date().toISOString(),
          time: new Date(data.timestamp || Date.now()).toLocaleTimeString(),
          timestamp: new Date(data.timestamp || Date.now()).getTime(),
          isRead: true, // Message reçu en temps réel = automatiquement lu si conversation ouverte
        };

        // Marquer immédiatement ce message spécifique comme lu dans Supabase
        if (supabaseUser?.id && data.messageId) {
          (async () => {
            try {
              await supabase
                .from('message_reads')
                .upsert({
                  message_id: messageId,
                  user_id: supabaseUser.id,
                  read_at: new Date().toISOString()
                }, { onConflict: 'message_id,user_id' });
            } catch (err) {
              console.error('Error marking new message as read:', err);
            }
          })();
        }

        return [...prev, newMessage];
      });
    };

    socket.on('new-message', handleNewMessage);

    // Nettoyage
    return () => {
      socket.emit('leave-conversation', conversationId);
      socket.off('new-message', handleNewMessage);
    };
  }, [conversationId, user?.id, supabaseUser?.id]);

  // Chargement initial
  useEffect(() => {
    loadMessages();
  }, [loadMessages]);

  return {
    messages,
    loading,
    error,
    sending,
    sendMessage,
    reload: loadMessages
  };
};

