import 'dart:async';
import 'dart:convert';
import 'package:unionflow_mobile_apps/core/network/api_client.dart';
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import '../models/dashboard_stats_model.dart';
import '../../../../core/storage/dashboard_cache_manager.dart';

/// Service de mode hors ligne avec synchronisation pour le Dashboard
class DashboardOfflineService {
  static const String _offlineQueueKey = 'dashboard_offline_queue';
  static const String _lastSyncKey = 'dashboard_last_sync';
  
  final DashboardCacheManager _cacheManager;
  final ApiClient _apiClient;
  final Connectivity _connectivity = Connectivity();
  
  SharedPreferences? _prefs;
  StreamSubscription<List<ConnectivityResult>>? _connectivitySubscription;
  Timer? _syncTimer;
  
  final StreamController<OfflineStatus> _statusController = 
      StreamController<OfflineStatus>.broadcast();
  final StreamController<SyncProgress> _syncController = 
      StreamController<SyncProgress>.broadcast();
  
  final List<OfflineAction> _pendingActions = [];
  bool _isOnline = true;
  bool _isSyncing = false;
  DateTime? _lastSyncTime;
  
  // Streams publics
  Stream<OfflineStatus> get statusStream => _statusController.stream;
  Stream<SyncProgress> get syncStream => _syncController.stream;
  
  DashboardOfflineService(this._cacheManager, this._apiClient);
  
  /// Initialise le service hors ligne
  Future<void> initialize() async {
    debugPrint('📱 Initialisation du service hors ligne...');
    
    _prefs = await SharedPreferences.getInstance();
    
    // Charger les actions en attente
    await _loadPendingActions();
    
    // Charger la dernière synchronisation
    _loadLastSyncTime();
    
    // Vérifier la connectivité initiale
    final connectivityResult = await _connectivity.checkConnectivity();
    _updateConnectivityStatus(connectivityResult);

    // Écouter les changements de connectivité
    _connectivitySubscription = _connectivity.onConnectivityChanged.listen(
      (List<ConnectivityResult> results) => _updateConnectivityStatus(results),
    );
    
    // Démarrer la synchronisation automatique
    _startAutoSync();
    
    debugPrint('✅ Service hors ligne initialisé');
  }
  
  /// Met à jour le statut de connectivité
  void _updateConnectivityStatus(dynamic result) {
    final wasOnline = _isOnline;
    if (result is List<ConnectivityResult>) {
      _isOnline = result.any((r) => r != ConnectivityResult.none);
    } else if (result is ConnectivityResult) {
      _isOnline = result != ConnectivityResult.none;
    } else {
      _isOnline = false;
    }
    
    debugPrint('🌐 Connectivité: ${_isOnline ? 'En ligne' : 'Hors ligne'}');
    
    _statusController.add(OfflineStatus(
      isOnline: _isOnline,
      pendingActionsCount: _pendingActions.length,
      lastSyncTime: _lastSyncTime,
    ));
    
    // Si on revient en ligne, synchroniser
    if (!wasOnline && _isOnline && _pendingActions.isNotEmpty) {
      _syncPendingActions();
    }
  }
  
  /// Démarre la synchronisation automatique
  void _startAutoSync() {
    _syncTimer = Timer.periodic(
      const Duration(minutes: 5),
      (_) {
        if (_isOnline && _pendingActions.isNotEmpty) {
          _syncPendingActions();
        }
      },
    );
  }
  
  /// Ajoute une action à la queue hors ligne
  Future<void> queueAction(OfflineAction action) async {
    _pendingActions.add(action);
    await _savePendingActions();
    
    debugPrint('📝 Action mise en queue: ${action.type} (${_pendingActions.length} en attente)');
    
    _statusController.add(OfflineStatus(
      isOnline: _isOnline,
      pendingActionsCount: _pendingActions.length,
      lastSyncTime: _lastSyncTime,
    ));
    
    // Si en ligne, essayer de synchroniser immédiatement
    if (_isOnline) {
      _syncPendingActions();
    }
  }
  
  /// Synchronise les actions en attente
  Future<void> _syncPendingActions() async {
    if (_isSyncing || _pendingActions.isEmpty || !_isOnline) {
      return;
    }
    
    _isSyncing = true;
    debugPrint('🔄 Début de la synchronisation (${_pendingActions.length} actions)');
    
    _syncController.add(SyncProgress(
      isActive: true,
      totalActions: _pendingActions.length,
      completedActions: 0,
      currentAction: _pendingActions.first.type.toString(),
    ));
    
    final actionsToSync = List<OfflineAction>.from(_pendingActions);
    int completedCount = 0;
    
    for (final action in actionsToSync) {
      try {
        await _executeAction(action);
        _pendingActions.remove(action);
        completedCount++;
        
        _syncController.add(SyncProgress(
          isActive: true,
          totalActions: actionsToSync.length,
          completedActions: completedCount,
          currentAction: completedCount < actionsToSync.length
              ? actionsToSync[completedCount].type.toString()
              : null,
        ));
        
        debugPrint('✅ Action synchronisée: ${action.type}');
        
      } catch (e) {
        debugPrint('❌ Erreur lors de la synchronisation de ${action.type}: $e');
        
        // Marquer l'action comme échouée si trop de tentatives
        action.retryCount++;
        if (action.retryCount >= 3) {
          _pendingActions.remove(action);
          debugPrint('🗑️ Action abandonnée après 3 tentatives: ${action.type}');
        }
      }
    }
    
    await _savePendingActions();
    _lastSyncTime = DateTime.now();
    await _saveLastSyncTime();
    
    _syncController.add(SyncProgress(
      isActive: false,
      totalActions: actionsToSync.length,
      completedActions: completedCount,
      currentAction: null,
    ));
    
    _statusController.add(OfflineStatus(
      isOnline: _isOnline,
      pendingActionsCount: _pendingActions.length,
      lastSyncTime: _lastSyncTime,
    ));
    
    _isSyncing = false;
    debugPrint('✅ Synchronisation terminée ($completedCount/${actionsToSync.length} réussies)');
  }
  
  /// Exécute une action spécifique
  Future<void> _executeAction(OfflineAction action) async {
    switch (action.type) {
      case OfflineActionType.refreshDashboard:
        await _syncDashboardData(action);
        break;
      case OfflineActionType.updatePreferences:
        await _syncUserPreferences(action);
        break;
      case OfflineActionType.markActivityRead:
        await _syncActivityRead(action);
        break;
      case OfflineActionType.joinEvent:
        await _syncEventJoin(action);
        break;
      case OfflineActionType.exportReport:
        await _syncReportExport(action);
        break;
    }
  }
  
  /// Synchronise les données du dashboard (rafraîchit le cache)
  Future<void> _syncDashboardData(OfflineAction action) async {
    final orgId = action.data['organizationId'] as String?;
    final userId = action.data['userId'] as String?;
    if (orgId == null || userId == null) return;

    final response = await _apiClient.get('/api/dashboard/stats', queryParameters: {
      'organisationId': orgId,
    });
    if (response.statusCode == 200 && response.data != null) {
      await _cacheManager.setKey(
        'dashboard_${orgId}_$userId',
        response.data as Map<String, dynamic>,
      );
    }
  }

  /// Synchronise les préférences utilisateur
  Future<void> _syncUserPreferences(OfflineAction action) async {
    final userId = action.data['userId'] as String?;
    final preferences = action.data['preferences'] as Map<String, dynamic>?;
    if (userId == null || preferences == null) return;

    await _apiClient.put('/api/membres/$userId/preferences', data: preferences);
  }

  /// Synchronise le marquage d'activité comme lue
  Future<void> _syncActivityRead(OfflineAction action) async {
    final activityId = action.data['activityId'] as String?;
    if (activityId == null) return;

    await _apiClient.put('/api/notifications/$activityId/read');
  }

  /// Synchronise l'inscription à un événement
  Future<void> _syncEventJoin(OfflineAction action) async {
    final eventId = action.data['eventId'] as String?;
    final membreId = action.data['membreId'] as String?;
    if (eventId == null || membreId == null) return;

    await _apiClient.post('/api/evenements/$eventId/inscription', data: {
      'membreId': membreId,
    });
  }

  /// Synchronise l'export de rapport
  Future<void> _syncReportExport(OfflineAction action) async {
    final reportType = action.data['reportType'] as String?;
    final params = action.data['params'] as Map<String, dynamic>?;
    if (reportType == null) return;

    await _apiClient.post('/api/export/$reportType', data: params ?? {});
  }
  
  /// Sauvegarde les actions en attente
  Future<void> _savePendingActions() async {
    if (_prefs == null) return;
    
    final actionsJson = _pendingActions
        .map((action) => action.toJson())
        .toList();
    
    await _prefs!.setString(_offlineQueueKey, jsonEncode(actionsJson));
  }
  
  /// Charge les actions en attente
  Future<void> _loadPendingActions() async {
    if (_prefs == null) return;
    
    final actionsJsonString = _prefs!.getString(_offlineQueueKey);
    if (actionsJsonString != null) {
      try {
        final actionsJson = jsonDecode(actionsJsonString) as List;
        _pendingActions.clear();
        _pendingActions.addAll(
          actionsJson.map((json) => OfflineAction.fromJson(json)),
        );
        
        debugPrint('📋 ${_pendingActions.length} actions chargées depuis le cache');
      } catch (e) {
        debugPrint('❌ Erreur lors du chargement des actions: $e');
        await _prefs!.remove(_offlineQueueKey);
      }
    }
  }
  
  /// Sauvegarde l'heure de dernière synchronisation
  Future<void> _saveLastSyncTime() async {
    if (_prefs == null || _lastSyncTime == null) return;
    
    await _prefs!.setInt(_lastSyncKey, _lastSyncTime!.millisecondsSinceEpoch);
  }
  
  /// Charge l'heure de dernière synchronisation
  void _loadLastSyncTime() {
    if (_prefs == null) return;
    
    final lastSyncMs = _prefs!.getInt(_lastSyncKey);
    if (lastSyncMs != null) {
      _lastSyncTime = DateTime.fromMillisecondsSinceEpoch(lastSyncMs);
    }
  }
  
  /// Force une synchronisation manuelle
  Future<void> forceSync() async {
    if (!_isOnline) {
      throw Exception('Impossible de synchroniser hors ligne');
    }
    
    await _syncPendingActions();
  }
  
  /// Obtient les données en mode hors ligne
  Future<DashboardDataModel?> getOfflineData(
    String organizationId,
    String userId,
  ) async {
    final m = _cacheManager.getKey<Map<String, dynamic>>('dashboard_${organizationId}_$userId');
    return m != null ? DashboardDataModel.fromJson(m) : null;
  }
  
  /// Vérifie si des données sont disponibles hors ligne
  Future<bool> hasOfflineData(String organizationId, String userId) async {
    final data = await getOfflineData(organizationId, userId);
    return data != null;
  }
  
  /// Obtient les statistiques du mode hors ligne
  OfflineStats getStats() {
    return OfflineStats(
      isOnline: _isOnline,
      pendingActionsCount: _pendingActions.length,
      lastSyncTime: _lastSyncTime,
      isSyncing: _isSyncing,
      cacheStats: _cacheManager.getCacheStats(),
    );
  }
  
  /// Nettoie les anciennes actions
  Future<void> cleanupOldActions() async {
    final cutoffTime = DateTime.now().subtract(const Duration(days: 7));
    
    _pendingActions.removeWhere((action) => 
        action.timestamp.isBefore(cutoffTime));
    
    await _savePendingActions();
  }
  
  /// Libère les ressources
  void dispose() {
    _connectivitySubscription?.cancel();
    _syncTimer?.cancel();
    _statusController.close();
    _syncController.close();
  }
}

/// Action hors ligne
class OfflineAction {
  final String id;
  final OfflineActionType type;
  final Map<String, dynamic> data;
  final DateTime timestamp;
  int retryCount;
  
  OfflineAction({
    required this.id,
    required this.type,
    required this.data,
    required this.timestamp,
    this.retryCount = 0,
  });
  
  factory OfflineAction.fromJson(Map<String, dynamic> json) {
    return OfflineAction(
      id: json['id'] as String,
      type: OfflineActionType.values.firstWhere(
        (t) => t.name == json['type'],
      ),
      data: json['data'] as Map<String, dynamic>,
      timestamp: DateTime.parse(json['timestamp'] as String),
      retryCount: json['retryCount'] as int? ?? 0,
    );
  }
  
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'type': type.name,
      'data': data,
      'timestamp': timestamp.toIso8601String(),
      'retryCount': retryCount,
    };
  }
}

/// Types d'actions hors ligne
enum OfflineActionType {
  refreshDashboard,
  updatePreferences,
  markActivityRead,
  joinEvent,
  exportReport,
}

/// Statut hors ligne
class OfflineStatus {
  final bool isOnline;
  final int pendingActionsCount;
  final DateTime? lastSyncTime;
  
  const OfflineStatus({
    required this.isOnline,
    required this.pendingActionsCount,
    this.lastSyncTime,
  });
  
  String get statusText {
    if (isOnline) {
      if (pendingActionsCount > 0) {
        return 'En ligne - $pendingActionsCount actions en attente';
      } else {
        return 'En ligne - Synchronisé';
      }
    } else {
      return 'Hors ligne - Mode cache activé';
    }
  }
}

/// Progression de synchronisation
class SyncProgress {
  final bool isActive;
  final int totalActions;
  final int completedActions;
  final String? currentAction;
  
  const SyncProgress({
    required this.isActive,
    required this.totalActions,
    required this.completedActions,
    this.currentAction,
  });
  
  double get progress {
    if (totalActions == 0) return 1.0;
    return completedActions / totalActions;
  }
  
  String get progressText {
    if (!isActive) return 'Synchronisation terminée';
    if (currentAction != null) {
      return 'Synchronisation: $currentAction ($completedActions/$totalActions)';
    }
    return 'Synchronisation en cours... ($completedActions/$totalActions)';
  }
}

/// Statistiques du mode hors ligne
class OfflineStats {
  final bool isOnline;
  final int pendingActionsCount;
  final DateTime? lastSyncTime;
  final bool isSyncing;
  final Map<String, dynamic> cacheStats;
  
  const OfflineStats({
    required this.isOnline,
    required this.pendingActionsCount,
    this.lastSyncTime,
    required this.isSyncing,
    required this.cacheStats,
  });
  
  String get lastSyncText {
    if (lastSyncTime == null) return 'Jamais synchronisé';
    
    final now = DateTime.now();
    final diff = now.difference(lastSyncTime!);
    
    if (diff.inMinutes < 1) return 'Synchronisé à l\'instant';
    if (diff.inMinutes < 60) return 'Synchronisé il y a ${diff.inMinutes}min';
    if (diff.inHours < 24) return 'Synchronisé il y a ${diff.inHours}h';
    return 'Synchronisé il y a ${diff.inDays}j';
  }
}
