/**
 * Hook React pour gérer les notifications temps réel
 */

import { useState, useEffect, useRef, useCallback } from 'react';
import { useAuth } from '@/contexts/AuthContext';

export interface Notification {
  id: string;
  type: string;
  title: string;
  message: string;
  priority: 'critical' | 'high' | 'medium' | 'low';
  timestamp: Date;
  data?: Record<string, any>;
  actions?: NotificationAction[];
  readAt?: Date;
  expiresAt?: Date;
}

export interface NotificationAction {
  id: string;
  label: string;
  action: 'navigate' | 'api_call' | 'dismiss';
  payload?: Record<string, any>;
}

interface UseNotificationsOptions {
  autoConnect?: boolean;
  maxRetries?: number;
  retryDelay?: number;
}

export function useNotifications(options: UseNotificationsOptions = {}) {
  const { user } = useAuth();
  const { 
    autoConnect = true, 
    maxRetries = 5, 
    retryDelay = 3000 
  } = options;

  const [notifications, setNotifications] = useState<Notification[]>([]);
  const [isConnected, setIsConnected] = useState(false);
  const [connectionError, setConnectionError] = useState<string | null>(null);
  const [unreadCount, setUnreadCount] = useState(0);

  const ws = useRef<WebSocket | null>(null);
  const reconnectAttempts = useRef(0);
  const reconnectTimeout = useRef<NodeJS.Timeout | null>(null);
  const heartbeatInterval = useRef<NodeJS.Timeout | null>(null);

  // Calculer le nombre de notifications non lues
  useEffect(() => {
    const count = notifications.filter(n => !n.readAt).length;
    setUnreadCount(count);
  }, [notifications]);

  const connect = useCallback(() => {
    if (!user || ws.current?.readyState === WebSocket.CONNECTING) {
      return;
    }

    try {
      const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
      const wsUrl = `${protocol}//${window.location.host}/ws?userId=${user.id}&role=${user.role}`;
      
      ws.current = new WebSocket(wsUrl);

      ws.current.onopen = () => {
        setIsConnected(true);
        setConnectionError(null);
        reconnectAttempts.current = 0;
        
        // Heartbeat pour maintenir la connexion
        heartbeatInterval.current = setInterval(() => {
          if (ws.current?.readyState === WebSocket.OPEN) {
            ws.current.send(JSON.stringify({ type: 'ping' }));
          }
        }, 30000);

        console.log('WebSocket connected for notifications');
      };

      ws.current.onmessage = (event) => {
        try {
          const message = JSON.parse(event.data);
          handleWebSocketMessage(message);
        } catch (error) {
          console.error('Error parsing WebSocket message:', error);
        }
      };

      ws.current.onclose = (event) => {
        setIsConnected(false);
        
        if (heartbeatInterval.current) {
          clearInterval(heartbeatInterval.current);
          heartbeatInterval.current = null;
        }

        if (event.wasClean) {
          console.log('WebSocket connection closed cleanly');
        } else {
          console.error('WebSocket connection lost');
          attemptReconnect();
        }
      };

      ws.current.onerror = (error) => {
        console.error('WebSocket error:', error);
        setConnectionError('Connexion WebSocket échouée');
      };

    } catch (error) {
      console.error('Error creating WebSocket connection:', error);
      setConnectionError('Impossible de se connecter aux notifications');
    }
  }, [user]);

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

    if (heartbeatInterval.current) {
      clearInterval(heartbeatInterval.current);
      heartbeatInterval.current = null;
    }

    if (ws.current) {
      ws.current.close(1000, 'Disconnect requested');
      ws.current = null;
    }

    setIsConnected(false);
  }, []);

  const attemptReconnect = useCallback(() => {
    if (reconnectAttempts.current >= maxRetries) {
      setConnectionError('Nombre maximum de tentatives de reconnexion atteint');
      return;
    }

    reconnectAttempts.current++;
    const delay = retryDelay * Math.pow(2, reconnectAttempts.current - 1); // Backoff exponentiel

    reconnectTimeout.current = setTimeout(() => {
      console.log(`Tentative de reconnexion ${reconnectAttempts.current}/${maxRetries}`);
      connect();
    }, delay);
  }, [connect, maxRetries, retryDelay]);

  const handleWebSocketMessage = useCallback((message: any) => {
    switch (message.type) {
      case 'notification':
        const notification: Notification = {
          ...message.data,
          timestamp: new Date(message.data.timestamp)
        };
        
        setNotifications(prev => [notification, ...prev].slice(0, 100)); // Garder 100 max
        
        // Notification audio pour les urgences critiques
        if (notification.priority === 'critical') {
          playNotificationSound();
          showBrowserNotification(notification);
        }
        break;

      case 'pending_notifications':
        const pendingNotifications = message.data.map((n: any) => ({
          ...n,
          timestamp: new Date(n.timestamp)
        }));
        setNotifications(prev => [...pendingNotifications, ...prev]);
        break;

      case 'courier_location':
        // Gérer les mises à jour de position des livreurs
        handleCourierLocationUpdate(message.data);
        break;

      case 'pong':
        // Réponse au ping - connexion active
        break;

      default:
        console.log('Unknown WebSocket message type:', message.type);
    }
  }, []);

  const playNotificationSound = useCallback(() => {
    try {
      // Son d'urgence pour les notifications critiques
      const audio = new Audio('/sounds/emergency-notification.mp3');
      audio.volume = 0.7;
      audio.play().catch(console.error);
    } catch (error) {
      console.error('Error playing notification sound:', error);
    }
  }, []);

  const showBrowserNotification = useCallback((notification: Notification) => {
    if (!('Notification' in window)) {
      return;
    }

    if (Notification.permission === 'granted') {
      const browserNotification = new Notification(notification.title, {
        body: notification.message,
        icon: '/icon-192x192.png',
        badge: '/icon-96x96.png',
        tag: notification.id,
        requireInteraction: notification.priority === 'critical',
        vibrate: notification.priority === 'critical' ? [200, 100, 200] : [100]
      });

      browserNotification.onclick = () => {
        window.focus();
        browserNotification.close();
        
        // Exécuter la première action si disponible
        if (notification.actions && notification.actions.length > 0) {
          executeNotificationAction(notification.actions[0]);
        }
      };

      // Auto-fermer après 10 secondes sauf pour les critiques
      if (notification.priority !== 'critical') {
        setTimeout(() => browserNotification.close(), 10000);
      }
    }
  }, []);

  const handleCourierLocationUpdate = useCallback((data: any) => {
    // Émettre un événement personnalisé pour les composants de tracking
    window.dispatchEvent(new CustomEvent('courierLocationUpdate', {
      detail: data
    }));
  }, []);

  // Actions sur les notifications
  const markAsRead = useCallback((notificationId: string) => {
    setNotifications(prev => 
      prev.map(n => 
        n.id === notificationId ? { ...n, readAt: new Date() } : n
      )
    );

    // Informer le serveur
    if (ws.current?.readyState === WebSocket.OPEN) {
      ws.current.send(JSON.stringify({
        type: 'mark_read',
        notificationId
      }));
    }
  }, []);

  const markAllAsRead = useCallback(() => {
    const unreadIds = notifications.filter(n => !n.readAt).map(n => n.id);
    
    setNotifications(prev => 
      prev.map(n => ({ ...n, readAt: n.readAt || new Date() }))
    );

    // Informer le serveur pour chaque notification
    if (ws.current?.readyState === WebSocket.OPEN) {
      unreadIds.forEach(id => {
        ws.current!.send(JSON.stringify({
          type: 'mark_read',
          notificationId: id
        }));
      });
    }
  }, [notifications]);

  const dismissNotification = useCallback((notificationId: string) => {
    setNotifications(prev => prev.filter(n => n.id !== notificationId));
  }, []);

  const executeNotificationAction = useCallback((action: NotificationAction) => {
    switch (action.action) {
      case 'navigate':
        if (action.payload?.path) {
          window.location.href = action.payload.path;
        }
        break;

      case 'api_call':
        // Appel API (à implémenter selon les besoins)
        console.log('API call action:', action.payload);
        break;

      case 'dismiss':
        if (action.payload?.notificationId) {
          dismissNotification(action.payload.notificationId);
        }
        break;
    }
  }, [dismissNotification]);

  const subscribe = useCallback((topics: string[]) => {
    if (ws.current?.readyState === WebSocket.OPEN) {
      ws.current.send(JSON.stringify({
        type: 'subscribe',
        topics
      }));
    }
  }, []);

  const unsubscribe = useCallback((topics: string[]) => {
    if (ws.current?.readyState === WebSocket.OPEN) {
      ws.current.send(JSON.stringify({
        type: 'unsubscribe',
        topics
      }));
    }
  }, []);

  // Demander la permission pour les notifications du navigateur
  const requestNotificationPermission = useCallback(async () => {
    if (!('Notification' in window)) {
      return 'not-supported';
    }

    if (Notification.permission === 'default') {
      const permission = await Notification.requestPermission();
      return permission;
    }

    return Notification.permission;
  }, []);

  // Connexion automatique
  useEffect(() => {
    if (autoConnect && user && !isConnected && !ws.current) {
      connect();
    }

    return () => {
      if (!autoConnect) {
        disconnect();
      }
    };
  }, [user, autoConnect, connect, disconnect, isConnected]);

  // Nettoyage lors du démontage
  useEffect(() => {
    return () => {
      disconnect();
    };
  }, [disconnect]);

  return {
    notifications,
    unreadCount,
    isConnected,
    connectionError,
    
    // Actions
    connect,
    disconnect,
    markAsRead,
    markAllAsRead,
    dismissNotification,
    executeNotificationAction,
    subscribe,
    unsubscribe,
    requestNotificationPermission,

    // Utilitaires
    getUnreadNotifications: () => notifications.filter(n => !n.readAt),
    getCriticalNotifications: () => notifications.filter(n => n.priority === 'critical' && !n.readAt),
    clearAll: () => setNotifications([])
  };
}