interface WebSocketMessage {
  type: string;
  data: any;
}

interface WebSocketService {
  connect: () => Promise<void>;
  disconnect: () => void;
  send: (message: any) => void;
  onMessage: (callback: (message: WebSocketMessage) => void) => void;
  onError: (callback: (error: Event) => void) => void;
  onOpen: (callback: () => void) => void;
  onClose: (callback: () => void) => void;
  isConnected: () => boolean;
  on: (event: string, callback: (...args: any[]) => void) => void; // EventEmitter-style API
  off: (event: string, callback: (...args: any[]) => void) => void;
  connectChat?: () => Promise<void>; // Alias pour compatibilité ChatInterface
  joinConversation: (conversationId: string) => void;
  leaveConversation: (conversationId: string) => void;
  sendMessage: (
    conversationId: string,
    content: string,
    parentId?: string,
  ) => void;
  startTyping: (conversationId: string) => void;
  stopTyping: (conversationId: string) => void;
  addReaction: (messageId: string, emoji: string) => void;
  removeReaction: (messageId: string, emoji: string) => void;
}

import { logger } from '@/utils/logger';

class WebSocketServiceImpl implements WebSocketService {
  private ws: WebSocket | null = null;
  private messageHandlers: Array<(message: WebSocketMessage) => void> = [];
  private errorHandlers: Array<(error: Event) => void> = [];
  private openHandlers: Array<() => void> = [];
  private closeHandlers: Array<() => void> = [];
  private reconnectAttempts = 0;
  private maxReconnectAttempts = 5;
  private reconnectDelay = 3000;

  async connect(): Promise<void> {
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      return;
    }

    // CORRECTION: Le serveur Rust expose le WebSocket sur /ws
    const wsUrl = (() => {
      const url = import.meta.env.VITE_WS_URL;
      if (!url) {
        if (import.meta.env.PROD) {
          throw new Error('VITE_WS_URL must be defined in production');
        }
        // Fallback uniquement en développement
        return 'ws://127.0.0.1:8081/ws';
      }
      // Convertir URL relative en URL absolue pour WebSocket
      if (url.startsWith('/')) {
        const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
        return `${protocol}//${window.location.host}${url}`;
      }
      return url;
    })();

    return new Promise((resolve, reject) => {
      try {
        this.ws = new WebSocket(wsUrl);

        this.ws.onopen = () => {
          this.reconnectAttempts = 0;
          this.openHandlers.forEach((handler) => handler());
          resolve();
        };

        this.ws.onmessage = (event) => {
          try {
            const message = JSON.parse(event.data);
            this.messageHandlers.forEach((handler) => handler(message));
          } catch (error) {
            logger.error('Failed to parse WebSocket message:', { error });
          }
        };

        this.ws.onerror = (error) => {
          this.errorHandlers.forEach((handler) => handler(error));
          // CORRECTION: Ne pas rejeter immédiatement, attendre onclose
          // Ne pas logger l'erreur ici car onclose sera appelé après avec plus de détails
          // reject(error);
        };

        this.ws.onclose = (_event) => {
          this.closeHandlers.forEach((handler) => handler());

          // CORRECTION DURABLE: Reconnexion intelligente avec backoff exponentiel
          // Ne pas spammer la console si le serveur est down
          if (this.reconnectAttempts < this.maxReconnectAttempts) {
            this.reconnectAttempts++;
            const delay =
              this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1); // Backoff exponentiel

            // Afficher un message seulement pour les premières tentatives ou si c'est la dernière
            if (this.reconnectAttempts === 1) {
              logger.warn(
                `[WebSocket] Connection closed. Chat server unavailable. Retrying in ${delay / 1000}s...`,
              );
            } else if (this.reconnectAttempts === this.maxReconnectAttempts) {
              logger.error(
                `[WebSocket] Max reconnection attempts (${this.maxReconnectAttempts}) reached. Chat server unavailable at ${wsUrl}. Please ensure the chat server is running.`,
              );
            }

            setTimeout(() => {
              this.connect().catch(() => {
                // Silently fail, retry will happen via reconnect logic
              });
            }, delay);
          } else {
            logger.error(
              `[WebSocket] Max reconnection attempts (${this.maxReconnectAttempts}) reached. Chat server unavailable at ${wsUrl}. Please ensure the chat server is running on port 8081.`,
            );
            reject(
              new Error('WebSocket connection failed after maximum retries'),
            );
          }
        };
      } catch (error) {
        reject(error);
      }
    });
  }

  disconnect(): void {
    if (this.ws) {
      this.ws.close();
      this.ws = null;
    }
  }

  send(message: any): void {
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(message));
    } else {
      logger.error('WebSocket is not connected');
    }
  }

  onMessage(callback: (message: WebSocketMessage) => void): void {
    this.messageHandlers.push(callback);
  }

  onError(callback: (error: Event) => void): void {
    this.errorHandlers.push(callback);
  }

  onOpen(callback: () => void): void {
    this.openHandlers.push(callback);
  }

  onClose(callback: () => void): void {
    this.closeHandlers.push(callback);
  }

  isConnected(): boolean {
    return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
  }

  removeMessageHandler(callback: (message: WebSocketMessage) => void): void {
    const index = this.messageHandlers.indexOf(callback);
    if (index > -1) {
      this.messageHandlers.splice(index, 1);
    }
  }

  // CORRECTION DURABLE: Méthode .on() pour compatibilité EventEmitter
  // Utilisée par ChatInterface et store/chat
  on(event: string, callback: (...args: any[]) => void): void {
    switch (event) {
      case 'message':
      case 'chat_message':
        this.onMessage(callback as (message: WebSocketMessage) => void);
        break;
      case 'connected':
      case 'chat_connected':
        this.onOpen(callback as () => void);
        break;
      case 'disconnected':
      case 'chat_disconnected':
        this.onClose(callback as () => void);
        break;
      case 'error':
      case 'chat_error':
        this.onError(callback as (error: Event) => void);
        break;
      default:
        logger.warn(`[WebSocket] Event '${event}' not supported`);
    }
  }

  off(event: string, callback: (...args: any[]) => void): void {
    switch (event) {
      case 'message':
      case 'chat_message':
        this.removeMessageHandler(
          callback as (message: WebSocketMessage) => void,
        );
        break;
      // TODO: Implement removal for other event types if needed
      default:
        // No-op for now as we don't track other handlers by reference in the same way
        // or they are specific arrays (openHandlers, etc) and we need a removeOpenHandler etc.
        break;
    }
  }

  connectChat(): Promise<void> {
    return this.connect();
  }

  joinConversation(conversationId: string): void {
    this.send({ type: 'join_conversation', conversation_id: conversationId });
  }

  joinRoom(room: string): void {
    this.joinConversation(room);
  }

  leaveConversation(conversationId: string): void {
    this.send({ type: 'leave_conversation', conversation_id: conversationId });
  }

  sendMessage(
    conversationId: string,
    content: string,
    parentId?: string,
  ): void {
    this.send({
      type: 'send_message',
      conversation_id: conversationId,
      content,
      parent_id: parentId,
    });
  }

  startTyping(conversationId: string): void {
    this.send({ type: 'start_typing', conversation_id: conversationId });
  }

  stopTyping(conversationId: string): void {
    this.send({ type: 'stop_typing', conversation_id: conversationId });
  }

  addReaction(messageId: string, emoji: string): void {
    this.send({ type: 'add_reaction', message_id: messageId, emoji });
  }

  removeReaction(messageId: string, emoji: string): void {
    this.send({ type: 'remove_reaction', message_id: messageId, emoji });
  }
}

export const websocketService = new WebSocketServiceImpl();

// CORRECTION DURABLE: Export alias pour compatibilité avec imports existants
// Certains fichiers utilisent 'wsService', d'autres 'websocketService'
export const wsService = websocketService;

// Helper hook for React components
export function useWebSocket() {
  const connect = () => websocketService.connect();
  const disconnect = () => websocketService.disconnect();
  const send = (message: any) => websocketService.send(message);
  const isConnected = () => websocketService.isConnected();

  return { connect, disconnect, send, isConnected };
}
