import 'dart:async';
import 'dart:convert';
import 'package:mqtt5_client/mqtt5_client.dart';
import 'package:mqtt5_client/mqtt5_server_client.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';

/// Service de gestion des notifications chat via MQTT
///
/// Utilise MQTT pour recevoir des notifications en temps réel
/// et afficher des notifications locales

class MqttNotificationService {
  static final MqttNotificationService _instance = MqttNotificationService._internal();
  factory MqttNotificationService() => _instance;
  MqttNotificationService._internal();

  late MqttServerClient _client;
  final FlutterLocalNotificationsPlugin _localNotifications = FlutterLocalNotificationsPlugin();
  
  // Configuration
  final String mqttHost;
  final int mqttPort;
  final String mqttUsername;
  final String mqttPassword;
  final String clientId;
  
  // État
  bool _initialized = false;
  String? _userId;
  StreamSubscription? _messageSubscription;
  
  // Callbacks
  Function(String messageId)? onMessageTap;
  Function(Map<String, dynamic>)? onNotificationReceived;
  
  MqttNotificationService({
    this.mqttHost = 'mqtt.geosector.fr',
    this.mqttPort = 1883,
    this.mqttUsername = '',
    this.mqttPassword = '',
    String? clientId,
  }) : clientId = clientId ?? 'geosector_chat_${DateTime.now().millisecondsSinceEpoch}';

  /// Initialise le service de notifications
  Future<void> initialize({required String userId}) async {
    if (_initialized) return;
    
    _userId = userId;
    
    // Initialiser les notifications locales
    await _initializeLocalNotifications();
    
    // Initialiser le client MQTT
    await _initializeMqttClient();
    
    _initialized = true;
  }

  /// Initialise le client MQTT
  Future<void> _initializeMqttClient() async {
    try {
      _client = MqttServerClient.withPort(mqttHost, clientId, mqttPort);
      
      _client.logging(on: kDebugMode);
      _client.keepAlivePeriod = 60;
      _client.onConnected = _onConnected;
      _client.onDisconnected = _onDisconnected;
      _client.onSubscribed = _onSubscribed;
      _client.autoReconnect = true;
      
      // Configurer les options de connexion
      final connMessage = MqttConnectMessage()
          .authenticateAs(mqttUsername, mqttPassword)
          .withClientIdentifier(clientId)
          .startClean()
          .keepAliveFor(60);
      
      _client.connectionMessage = connMessage;
      
      // Se connecter
      await _connect();
      
    } catch (e) {
      debugPrint('Erreur lors de l\'initialisation MQTT : $e');
      rethrow;
    }
  }

  /// Se connecte au broker MQTT
  Future<void> _connect() async {
    try {
      await _client.connect();
    } catch (e) {
      debugPrint('Erreur de connexion MQTT : $e');
      _client.disconnect();
      rethrow;
    }
  }

  /// Callback lors de la connexion
  void _onConnected() {
    debugPrint('Connecté au broker MQTT');
    
    // S'abonner aux topics de l'utilisateur
    if (_userId != null) {
      _subscribeToUserTopics(_userId!);
    }
    
    // Écouter les messages
    _messageSubscription = _client.updates?.listen(_onMessageReceived);
  }

  /// Callback lors de la déconnexion
  void _onDisconnected() {
    debugPrint('Déconnecté du broker MQTT');
    
    // Tenter une reconnexion
    if (_client.autoReconnect) {
      Future.delayed(const Duration(seconds: 5), () {
        _connect();
      });
    }
  }

  /// Callback lors de l'abonnement
  void _onSubscribed(MqttSubscription subscription) {
    debugPrint('Abonné au topic : ${subscription.topic.rawTopic}');
  }

  /// S'abonner aux topics de l'utilisateur
  void _subscribeToUserTopics(String userId) {
    // Topic pour les messages personnels
    _client.subscribe('chat/user/$userId/messages', MqttQos.atLeastOnce);
    
    // Topic pour les annonces
    _client.subscribe('chat/announcement', MqttQos.atLeastOnce);
    
    // Topic pour les groupes de l'utilisateur (si disponibles)
    _client.subscribe('chat/user/$userId/groups/+', MqttQos.atLeastOnce);
  }

  /// Gère les messages reçus
  void _onMessageReceived(List<MqttReceivedMessage<MqttMessage>> messages) {
    for (var message in messages) {
      final topic = message.topic;
      final payload = message.payload as MqttPublishMessage;
      final messageText = MqttUtilities.bytesToStringAsString(payload.payload.message!);
      
      try {
        final data = jsonDecode(messageText) as Map<String, dynamic>;
        _handleNotification(topic, data);
      } catch (e) {
        debugPrint('Erreur lors du décodage du message : $e');
      }
    }
  }

  /// Traite la notification reçue
  Future<void> _handleNotification(String topic, Map<String, dynamic> data) async {
    // Vérifier les paramètres de notification de l'utilisateur
    if (!await _shouldShowNotification(data)) {
      return;
    }
    
    String title = '';
    String body = '';
    String messageId = '';
    String conversationId = '';
    
    if (topic.startsWith('chat/user/')) {
      // Message personnel
      title = data['senderName'] ?? 'Nouveau message';
      body = data['content'] ?? '';
      messageId = data['messageId'] ?? '';
      conversationId = data['conversationId'] ?? '';
    } else if (topic.startsWith('chat/announcement')) {
      // Annonce
      title = data['title'] ?? 'Annonce';
      body = data['content'] ?? '';
      messageId = data['messageId'] ?? '';
      conversationId = data['conversationId'] ?? '';
    }
    
    // Afficher la notification locale
    await _showLocalNotification(
      title: title,
      body: body,
      payload: jsonEncode({
        'messageId': messageId,
        'conversationId': conversationId,
      }),
    );
    
    // Appeler le callback si défini
    onNotificationReceived?.call(data);
  }

  /// Vérifie si la notification doit être affichée
  Future<bool> _shouldShowNotification(Map<String, dynamic> data) async {
    // TODO: Vérifier les paramètres de notification de l'utilisateur
    // - Notifications désactivées
    // - Conversation en silencieux
    // - Mode Ne pas déranger
    return true;
  }

  /// Initialise les notifications locales
  Future<void> _initializeLocalNotifications() async {
    const androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
    const iosSettings = DarwinInitializationSettings(
      requestAlertPermission: true,
      requestBadgePermission: true,
      requestSoundPermission: true,
    );
    
    const initSettings = InitializationSettings(
      android: androidSettings,
      iOS: iosSettings,
    );
    
    await _localNotifications.initialize(
      initSettings,
      onDidReceiveNotificationResponse: _onNotificationTap,
    );
  }

  /// Affiche une notification locale
  Future<void> _showLocalNotification({
    required String title,
    required String body,
    required String payload,
  }) async {
    const androidDetails = AndroidNotificationDetails(
      'chat_messages',
      'Messages de chat',
      channelDescription: 'Notifications pour les nouveaux messages de chat',
      importance: Importance.high,
      priority: Priority.high,
      icon: '@mipmap/ic_launcher',
    );
    
    const iosDetails = DarwinNotificationDetails(
      presentAlert: true,
      presentBadge: true,
      presentSound: true,
    );
    
    const notificationDetails = NotificationDetails(
      android: androidDetails,
      iOS: iosDetails,
    );
    
    await _localNotifications.show(
      DateTime.now().microsecondsSinceEpoch,
      title,
      body,
      notificationDetails,
      payload: payload,
    );
  }

  /// Handler pour le clic sur une notification
  void _onNotificationTap(NotificationResponse response) {
    final payload = response.payload;
    if (payload != null) {
      try {
        final data = jsonDecode(payload) as Map<String, dynamic>;
        final messageId = data['messageId'] as String?;
        if (messageId != null) {
          onMessageTap?.call(messageId);
        }
      } catch (e) {
        debugPrint('Erreur lors du traitement du clic sur notification : $e');
      }
    }
  }

  /// Publie un message MQTT
  Future<void> publishMessage(String topic, Map<String, dynamic> message) async {
    if (_client.connectionStatus?.state != MqttConnectionState.connected) {
      await _connect();
    }
    
    final messagePayload = jsonEncode(message);
    final builder = MqttPayloadBuilder();
    builder.addString(messagePayload);
    
    _client.publishMessage(topic, MqttQos.atLeastOnce, builder.payload!);
  }

  /// S'abonner à une conversation spécifique
  Future<void> subscribeToConversation(String conversationId) async {
    if (_client.connectionStatus?.state == MqttConnectionState.connected) {
      _client.subscribe('chat/conversation/$conversationId', MqttQos.atLeastOnce);
    }
  }

  /// Se désabonner d'une conversation
  Future<void> unsubscribeFromConversation(String conversationId) async {
    if (_client.connectionStatus?.state == MqttConnectionState.connected) {
      _client.unsubscribeStringTopic('chat/conversation/$conversationId');
    }
  }

  /// Désactive temporairement les notifications
  void pauseNotifications() {
    _client.pause();
  }

  /// Réactive les notifications
  void resumeNotifications() {
    _client.resume();
  }

  /// Libère les ressources
  void dispose() {
    _messageSubscription?.cancel();
    _client.disconnect();
    _initialized = false;
  }
}
