import { useEffect, useRef, useState, useCallback } from 'react';
import { useAuthStore } from '@/features/auth/store/authStore';
import { useUser } from '@/features/auth/hooks/useUser';
import { useChatStore } from '../store/chatStore';
import { apiClient } from '@/services/api/client';
import { OutgoingMessage, IncomingMessage } from '../types';
import { v4 as uuidv4 } from 'uuid'; // For message IDs
import type { UseChatReturn } from '@/hooks/types';
import { logger } from '@/utils/logger';

/**
 * Hook pour gérer le chat WebSocket
 * FE-TYPE-012: Fully typed hook return
 */
export const useChat = (): UseChatReturn => {
  const { data: user } = useUser();
  const userId = user?.id;
  // const _username = user?.username;
  const {
    wsToken,
    wsUrl,
    wsStatus,
    setWsStatus,
    addMessage,
    currentConversationId,
    loadMessages,
    addReaction,
    removeReaction,
    setUserTyping,
  } = useChatStore();

  const ws = useRef<WebSocket | null>(null);
  // CRITIQUE FIX #9: Utiliser un useRef pour stocker le compteur d'erreurs de manière persistante
  // au lieu de le stocker sur ws.current qui peut être réinitialisé
  const errorCountRef = useRef(0);
  // Queue for messages to send (reserved for future use)
  const [_messagesToSend, setMessagesToSend] = useState<OutgoingMessage[]>([]);

  const connect = useCallback(() => {
    if (!wsToken || !wsUrl || ws.current?.readyState === WebSocket.OPEN) return;

    // CRITIQUE FIX #9: Vérifier si le serveur WebSocket est disponible avant de tenter la connexion
    // En développement, si le serveur n'est pas démarré, limiter les tentatives
    if (import.meta.env.DEV && wsUrl.includes('127.0.0.1:8081')) {
      // En dev, vérifier si on a déjà eu trop d'erreurs de connexion
      if (errorCountRef.current >= 3) {
        // Trop d'erreurs, ne pas essayer de se connecter pour éviter le spam console
        setWsStatus('disconnected');
        return;
      }
    }

    // CRITIQUE FIX #39: Nettoyer la connexion précédente si elle existe
    if (ws.current) {
      ws.current.onopen = null;
      ws.current.onmessage = null;
      ws.current.onclose = null;
      ws.current.onerror = null;
      if (
        ws.current.readyState === WebSocket.OPEN ||
        ws.current.readyState === WebSocket.CONNECTING
      ) {
        ws.current.close();
      }
    }

    setWsStatus('connecting');
    const fullWsUrl = `${wsUrl}?token=${wsToken}`; // Assuming WS server is at root of wsUrl
    ws.current = new WebSocket(fullWsUrl);

    // CRITIQUE FIX #39: Stocker les références aux handlers pour pouvoir les nettoyer
    const handleOpen = () => {
      setWsStatus('connected');
      // CRITIQUE FIX #9: Réinitialiser le compteur d'erreurs en cas de succès
      errorCountRef.current = 0;
      // WebSocket connection successful - no logging needed in production
      // Send any queued messages
      setMessagesToSend((prev) => {
        prev.forEach((msg) => ws.current?.send(JSON.stringify(msg)));
        return [];
      });
    };

    const handleMessage = (event: MessageEvent) => {
      const data = JSON.parse(event.data);
      if (data.type === 'NewMessage') {
        const message: IncomingMessage = data;
        if (
          message.conversation_id === currentConversationId &&
          message.message_id &&
          message.sender_id &&
          message.content &&
          message.created_at
        ) {
          addMessage({
            id: message.message_id,
            conversation_id: message.conversation_id,
            sender_id: message.sender_id,
            sender_username: message.sender_username || 'Unknown',
            content: message.content,
            created_at: message.created_at,
            attachments: message.attachments,
          });
        }
      } else if (data.type === 'ReactionAdded') {
        const reaction: IncomingMessage = data;
        if (reaction.message_id && reaction.user_id && reaction.emoji) {
          addReaction(
            reaction.conversation_id,
            reaction.message_id,
            reaction.user_id,
            reaction.emoji,
          );
        }
      } else if (data.type === 'ReactionRemoved') {
        const reaction: IncomingMessage = data;
        if (reaction.message_id && reaction.user_id) {
          removeReaction(
            reaction.conversation_id,
            reaction.message_id,
            reaction.user_id,
          );
        }
      } else if (data.type === 'UserTyping') {
        const typing: IncomingMessage = data;
        if (typing.user_id) {
          setUserTyping(
            typing.conversation_id,
            typing.user_id,
            typing.is_typing ?? false,
          );
        }
      }
      // Handle other incoming message types (ActionConfirmed, Error, Pong)
    };

    const handleClose = () => {
      setWsStatus('disconnected');
      // WebSocket disconnected - no logging needed in production
      // Optional: Reconnect logic
    };

    const handleError = (error: Event) => {
      setWsStatus('error');
      // CRITIQUE FIX #9: Limiter les logs d'erreur pour éviter le spam console
      // En développement, si le serveur n'est pas démarré, limiter à 3 erreurs max
      errorCountRef.current += 1;
      if (errorCountRef.current <= 3) {
        if (import.meta.env.DEV) {
          logger.warn(
            `[WebSocket] Connexion échouée (${errorCountRef.current}/3). Le serveur WebSocket n'est peut-être pas démarré.`,
            {
              error: error instanceof Error ? error.message : String(error),
              stack: error instanceof Error ? error.stack : undefined,
            },
          );
        } else {
          logger.error('WebSocket error', {
            error: error instanceof Error ? error.message : String(error),
            stack: error instanceof Error ? error.stack : undefined,
          });
        }
      }
      // Don't close immediately - let onclose handle it
      // ws.current?.close();
    };

    // CRITIQUE FIX #39: Attacher les handlers
    ws.current.onopen = handleOpen;
    ws.current.onmessage = handleMessage;
    ws.current.onclose = handleClose;
    ws.current.onerror = handleError;
  }, [
    wsToken,
    wsUrl,
    setWsStatus,
    addMessage,
    currentConversationId,
    addReaction,
    removeReaction,
    setUserTyping,
  ]);

  const disconnect = useCallback(() => {
    // CRITIQUE FIX #39: Nettoyer proprement les event handlers avant de fermer
    if (ws.current) {
      ws.current.onopen = null;
      ws.current.onmessage = null;
      ws.current.onclose = null;
      ws.current.onerror = null;

      if (
        ws.current.readyState === WebSocket.OPEN ||
        ws.current.readyState === WebSocket.CONNECTING
      ) {
        ws.current.close();
      }

      ws.current = null;
      setWsStatus('disconnected');
    }
  }, [setWsStatus]);

  // FE-BUG-003: Add a ref to track reconnection attempts and avoid infinite loops
  const reconnectCount = useRef(0);
  const maxReconnects = 5;

  useEffect(() => {
    let timer: NodeJS.Timeout | undefined;

    if (
      wsToken &&
      wsUrl &&
      wsStatus === 'disconnected' &&
      reconnectCount.current < maxReconnects
    ) {
      timer = setTimeout(
        () => {
          reconnectCount.current++;
          connect();
        },
        1000 * Math.pow(2, reconnectCount.current),
      ); // Exponential backoff
    }

    if (wsStatus === 'connected') {
      reconnectCount.current = 0; // Reset on success
    }

    return () => {
      if (timer) {
        clearTimeout(timer);
      }
    };
  }, [wsToken, wsUrl, wsStatus, connect]);

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

  const sendMessage = useCallback(
    (content: string, attachments?: import('../types').MessageAttachment[]) => {
      if (
        !ws.current ||
        ws.current.readyState !== WebSocket.OPEN ||
        !currentConversationId ||
        !userId
      ) {
        // WebSocket not ready - message will be queued
        logger.warn(
          'WebSocket not open or missing conversation/user ID. Message queued.',
          {
            conversationId: currentConversationId,
            userId,
          },
        );
        setMessagesToSend((prev) => [
          ...prev,
          {
            type: 'SendMessage',
            conversation_id: currentConversationId || uuidv4(),
            content,
            parent_message_id: null,
            attachments,
          } as OutgoingMessage,
        ]);
        return;
      }

      const message: OutgoingMessage = {
        type: 'SendMessage',
        conversation_id: currentConversationId,
        content,
        parent_message_id: null,
        attachments,
      };
      ws.current.send(JSON.stringify(message));
    },
    [currentConversationId, userId],
  );

  // TODO: Add fetchHistory function
  const fetchHistory = useCallback(
    async (conversationId: string) => {
      try {
        const response = await apiClient.get(
          `/conversations/${conversationId}/history`,
        );
        loadMessages(conversationId, response.data.messages);
      } catch (error) {
        logger.error('Failed to fetch chat history', {
          error: error instanceof Error ? error.message : String(error),
          stack: error instanceof Error ? error.stack : undefined,
          conversationId,
        });
      }
    },
    [loadMessages],
  );

  const addReactionFunc = useCallback(
    (messageId: string, emoji: string) => {
      if (ws.current?.readyState === WebSocket.OPEN && currentConversationId) {
        ws.current.send(
          JSON.stringify({
            type: 'AddReaction',
            conversation_id: currentConversationId,
            message_id: messageId,
            emoji,
          } as OutgoingMessage),
        );
      }
    },
    [currentConversationId],
  );

  const removeReactionFunc = useCallback(
    (messageId: string) => {
      if (ws.current?.readyState === WebSocket.OPEN && currentConversationId) {
        ws.current.send(
          JSON.stringify({
            type: 'RemoveReaction',
            conversation_id: currentConversationId,
            message_id: messageId,
          } as OutgoingMessage),
        );
      }
    },
    [currentConversationId],
  );

  const setTyping = useCallback(
    (isTyping: boolean) => {
      if (ws.current?.readyState === WebSocket.OPEN && currentConversationId) {
        ws.current.send(
          JSON.stringify({
            type: 'Typing',
            conversation_id: currentConversationId,
            is_typing: isTyping,
          } as OutgoingMessage),
        );
      }
    },
    [currentConversationId],
  );

  return {
    wsStatus,
    connect,
    disconnect,
    sendMessage,
    fetchHistory,
    addReaction: addReactionFunc,
    removeReaction: removeReactionFunc,
    setTyping,
  };
};
