import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import '../models/dashboard_stats_model.dart';
import '../../config/dashboard_config.dart';
import '../../../../core/config/environment.dart';

/// Service de notifications temps réel pour le Dashboard
class DashboardNotificationService {
  static String get _wsEndpoint => AppConfig.wsDashboardUrl;
  
  WebSocketChannel? _channel;
  StreamSubscription? _subscription;
  Timer? _reconnectTimer;
  Timer? _heartbeatTimer;
  
  bool _isConnected = false;
  bool _shouldReconnect = true;
  int _reconnectAttempts = 0;
  static const int _maxReconnectAttempts = 5;
  static const Duration _reconnectDelay = Duration(seconds: 5);
  static const Duration _heartbeatInterval = Duration(seconds: 30);
  
  // Streams pour les différents types de notifications
  final StreamController<DashboardStatsModel> _statsController = 
      StreamController<DashboardStatsModel>.broadcast();
  final StreamController<RecentActivityModel> _activityController = 
      StreamController<RecentActivityModel>.broadcast();
  final StreamController<UpcomingEventModel> _eventController = 
      StreamController<UpcomingEventModel>.broadcast();
  final StreamController<DashboardNotification> _notificationController = 
      StreamController<DashboardNotification>.broadcast();
  final StreamController<ConnectionStatus> _connectionController = 
      StreamController<ConnectionStatus>.broadcast();
  
  // Getters pour les streams
  Stream<DashboardStatsModel> get statsStream => _statsController.stream;
  Stream<RecentActivityModel> get activityStream => _activityController.stream;
  Stream<UpcomingEventModel> get eventStream => _eventController.stream;
  Stream<DashboardNotification> get notificationStream => _notificationController.stream;
  Stream<ConnectionStatus> get connectionStream => _connectionController.stream;
  
  /// Initialise le service de notifications
  Future<void> initialize(String organizationId, String userId) async {
    if (!DashboardConfig.enableNotifications) {
      debugPrint('📱 Notifications désactivées dans la configuration');
      return;
    }
    
    debugPrint('📱 Initialisation du service de notifications...');
    await _connect(organizationId, userId);
  }
  
  /// Établit la connexion WebSocket
  Future<void> _connect(String organizationId, String userId) async {
    if (_isConnected) return;
    
    try {
      final uri = Uri.parse('$_wsEndpoint?orgId=$organizationId&userId=$userId');
      _channel = WebSocketChannel.connect(uri);
      
      debugPrint('📱 Connexion WebSocket en cours...');
      _connectionController.add(ConnectionStatus.connecting);
      
      // Écouter les messages
      _subscription = _channel!.stream.listen(
        _handleMessage,
        onError: _handleError,
        onDone: _handleDisconnection,
      );
      
      _isConnected = true;
      _reconnectAttempts = 0;
      _connectionController.add(ConnectionStatus.connected);
      
      // Démarrer le heartbeat
      _startHeartbeat();
      
      debugPrint('✅ Connexion WebSocket établie');
      
    } catch (e) {
      debugPrint('❌ Erreur de connexion WebSocket: $e');
      _connectionController.add(ConnectionStatus.error);
      _scheduleReconnect(organizationId, userId);
    }
  }
  
  /// Gère les messages reçus
  void _handleMessage(dynamic message) {
    try {
      final data = jsonDecode(message as String);
      final type = data['type'] as String?;
      final payload = data['payload'];
      
      debugPrint('📨 Message reçu: $type');
      
      switch (type) {
        case 'stats_update':
          final stats = DashboardStatsModel.fromJson(payload);
          _statsController.add(stats);
          break;
          
        case 'new_activity':
          final activity = RecentActivityModel.fromJson(payload);
          _activityController.add(activity);
          break;
          
        case 'event_update':
          final event = UpcomingEventModel.fromJson(payload);
          _eventController.add(event);
          break;
          
        case 'notification':
          final notification = DashboardNotification.fromJson(payload);
          _notificationController.add(notification);
          break;
          
        case 'pong':
          // Réponse au heartbeat
          debugPrint('💓 Heartbeat reçu');
          break;
          
        default:
          debugPrint('⚠️ Type de message inconnu: $type');
      }
      
    } catch (e) {
      debugPrint('❌ Erreur de parsing du message: $e');
    }
  }
  
  /// Gère les erreurs de connexion
  void _handleError(error) {
    debugPrint('❌ Erreur WebSocket: $error');
    _isConnected = false;
    _connectionController.add(ConnectionStatus.error);
  }
  
  /// Gère la déconnexion
  void _handleDisconnection() {
    debugPrint('🔌 Connexion WebSocket fermée');
    _isConnected = false;
    _connectionController.add(ConnectionStatus.disconnected);
    
    if (_shouldReconnect) {
      // Programmer une reconnexion
      _scheduleReconnect('', ''); // Les IDs seront récupérés du contexte
    }
  }
  
  /// Programme une tentative de reconnexion
  void _scheduleReconnect(String organizationId, String userId) {
    if (_reconnectAttempts >= _maxReconnectAttempts) {
      debugPrint('❌ Nombre maximum de tentatives de reconnexion atteint');
      _connectionController.add(ConnectionStatus.failed);
      return;
    }
    
    _reconnectAttempts++;
    final delay = _reconnectDelay * _reconnectAttempts;
    
    debugPrint('🔄 Reconnexion programmée dans ${delay.inSeconds}s (tentative $_reconnectAttempts)');
    
    _reconnectTimer = Timer(delay, () {
      if (_shouldReconnect) {
        _connect(organizationId, userId);
      }
    });
  }
  
  /// Démarre le heartbeat
  void _startHeartbeat() {
    _heartbeatTimer = Timer.periodic(_heartbeatInterval, (timer) {
      if (_isConnected && _channel != null) {
        try {
          _channel!.sink.add(jsonEncode({
            'type': 'ping',
            'timestamp': DateTime.now().toIso8601String(),
          }));
        } catch (e) {
          debugPrint('❌ Erreur lors de l\'envoi du heartbeat: $e');
        }
      }
    });
  }
  
  /// Envoie une demande de rafraîchissement
  void requestRefresh(String organizationId, String userId) {
    if (_isConnected && _channel != null) {
      try {
        _channel!.sink.add(jsonEncode({
          'type': 'refresh_request',
          'payload': {
            'organizationId': organizationId,
            'userId': userId,
            'timestamp': DateTime.now().toIso8601String(),
          },
        }));
        debugPrint('📤 Demande de rafraîchissement envoyée');
      } catch (e) {
        debugPrint('❌ Erreur lors de l\'envoi de la demande: $e');
      }
    }
  }
  
  /// S'abonne aux notifications pour un type spécifique
  void subscribeToNotifications(List<String> notificationTypes) {
    if (_isConnected && _channel != null) {
      try {
        _channel!.sink.add(jsonEncode({
          'type': 'subscribe',
          'payload': {
            'notificationTypes': notificationTypes,
            'timestamp': DateTime.now().toIso8601String(),
          },
        }));
        debugPrint('📋 Abonnement aux notifications: $notificationTypes');
      } catch (e) {
        debugPrint('❌ Erreur lors de l\'abonnement: $e');
      }
    }
  }
  
  /// Se désabonne des notifications
  void unsubscribeFromNotifications(List<String> notificationTypes) {
    if (_isConnected && _channel != null) {
      try {
        _channel!.sink.add(jsonEncode({
          'type': 'unsubscribe',
          'payload': {
            'notificationTypes': notificationTypes,
            'timestamp': DateTime.now().toIso8601String(),
          },
        }));
        debugPrint('📋 Désabonnement des notifications: $notificationTypes');
      } catch (e) {
        debugPrint('❌ Erreur lors du désabonnement: $e');
      }
    }
  }
  
  /// Obtient le statut de la connexion
  bool get isConnected => _isConnected;
  
  /// Obtient le nombre de tentatives de reconnexion
  int get reconnectAttempts => _reconnectAttempts;
  
  /// Force une reconnexion
  Future<void> reconnect(String organizationId, String userId) async {
    await disconnect();
    _reconnectAttempts = 0;
    await _connect(organizationId, userId);
  }
  
  /// Déconnecte le service
  Future<void> disconnect() async {
    _shouldReconnect = false;
    
    _reconnectTimer?.cancel();
    _heartbeatTimer?.cancel();
    
    if (_channel != null) {
      await _channel!.sink.close();
      _channel = null;
    }
    
    await _subscription?.cancel();
    _subscription = null;
    
    _isConnected = false;
    _connectionController.add(ConnectionStatus.disconnected);
    
    debugPrint('🔌 Service de notifications déconnecté');
  }
  
  /// Libère les ressources
  void dispose() {
    disconnect();
    
    _statsController.close();
    _activityController.close();
    _eventController.close();
    _notificationController.close();
    _connectionController.close();
  }
}

/// Statut de la connexion
enum ConnectionStatus {
  disconnected,
  connecting,
  connected,
  error,
  failed,
}

/// Notification du dashboard
class DashboardNotification {
  final String id;
  final String type;
  final String title;
  final String message;
  final NotificationPriority priority;
  final DateTime timestamp;
  final Map<String, dynamic>? data;
  final String? actionUrl;
  final bool isRead;
  
  const DashboardNotification({
    required this.id,
    required this.type,
    required this.title,
    required this.message,
    required this.priority,
    required this.timestamp,
    this.data,
    this.actionUrl,
    this.isRead = false,
  });
  
  factory DashboardNotification.fromJson(Map<String, dynamic> json) {
    return DashboardNotification(
      id: json['id'] as String,
      type: json['type'] as String,
      title: json['title'] as String,
      message: json['message'] as String,
      priority: NotificationPriority.values.firstWhere(
        (p) => p.name == json['priority'],
        orElse: () => NotificationPriority.normal,
      ),
      timestamp: DateTime.parse(json['timestamp'] as String),
      data: json['data'] as Map<String, dynamic>?,
      actionUrl: json['actionUrl'] as String?,
      isRead: json['isRead'] as bool? ?? false,
    );
  }
  
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'type': type,
      'title': title,
      'message': message,
      'priority': priority.name,
      'timestamp': timestamp.toIso8601String(),
      'data': data,
      'actionUrl': actionUrl,
      'isRead': isRead,
    };
  }
  
  /// Obtient l'icône pour le type de notification
  String get icon {
    switch (type) {
      case 'new_member':
        return '👤';
      case 'new_event':
        return '📅';
      case 'contribution':
        return '💰';
      case 'urgent':
        return '🚨';
      case 'system':
        return '⚙️';
      default:
        return '📢';
    }
  }
  
  /// Obtient la couleur pour la priorité
  String get priorityColor {
    switch (priority) {
      case NotificationPriority.low:
        return '#6B7280';
      case NotificationPriority.normal:
        return '#3B82F6';
      case NotificationPriority.high:
        return '#F59E0B';
      case NotificationPriority.urgent:
        return '#EF4444';
    }
  }
}

/// Priorité des notifications
enum NotificationPriority {
  low,
  normal,
  high,
  urgent,
}
