import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import '../utils/logger.dart';

/// Service de cache stratégique avec TTL (Time To Live)
/// pour optimiser les performances et réduire les appels API
class CacheService {
  final SharedPreferences _prefs;

  CacheService(this._prefs);

  /// Clés de cache avec leur TTL en secondes
  static const Map<String, int> _cacheTTL = {
    'dashboard_stats': 300, // 5 minutes
    'parametres_lcb_ft': 1800, // 30 minutes
    'user_profile': 600, // 10 minutes
    'organisations': 3600, // 1 heure
    'notifications_count': 60, // 1 minute
  };

  /// Met en cache une valeur avec un TTL automatique selon la clé
  Future<bool> set(String key, dynamic value) async {
    try {
      final cacheData = {
        'value': value,
        'timestamp': DateTime.now().millisecondsSinceEpoch,
      };

      final jsonString = json.encode(cacheData);
      final success = await _prefs.setString(key, jsonString);

      if (success) {
        AppLogger.debug('Cache set: $key (TTL: ${_getTTL(key)}s)');
      }

      return success;
    } catch (e) {
      AppLogger.error('Erreur lors de la mise en cache de $key', error: e);
      return false;
    }
  }

  /// Récupère une valeur depuis le cache si elle n'est pas expirée
  /// Retourne null si la clé n'existe pas ou si le cache est expiré
  T? get<T>(String key) {
    try {
      final jsonString = _prefs.getString(key);
      if (jsonString == null) {
        return null;
      }

      final cacheData = json.decode(jsonString) as Map<String, dynamic>;
      final timestamp = cacheData['timestamp'] as int;
      final now = DateTime.now().millisecondsSinceEpoch;

      // Vérifier si le cache a expiré
      final ttl = _getTTL(key) * 1000; // Convertir en millisecondes
      if (now - timestamp > ttl) {
        AppLogger.debug('Cache expiré: $key');
        remove(key); // Nettoyer
        return null;
      }

      final value = cacheData['value'];
      AppLogger.debug('Cache hit: $key');
      return value as T;
    } catch (e) {
      AppLogger.error('Erreur lors de la lecture du cache $key', error: e);
      return null;
    }
  }

  /// Récupère une valeur String depuis le cache
  String? getString(String key) => get<String>(key);

  /// Récupère une valeur Map depuis le cache
  Map<String, dynamic>? getMap(String key) {
    final value = get<Map<String, dynamic>>(key);
    if (value == null) return null;
    return Map<String, dynamic>.from(value);
  }

  /// Récupère une valeur List depuis le cache
  List<dynamic>? getList(String key) {
    final value = get<List<dynamic>>(key);
    if (value == null) return null;
    return List<dynamic>.from(value);
  }

  /// Supprime une clé du cache
  Future<bool> remove(String key) async {
    try {
      return await _prefs.remove(key);
    } catch (e) {
      AppLogger.error('Erreur lors de la suppression du cache $key', error: e);
      return false;
    }
  }

  /// Nettoie toutes les clés d'un préfixe donné
  Future<void> clearByPrefix(String prefix) async {
    try {
      final keys = _prefs.getKeys();
      final keysToRemove = keys.where((k) => k.startsWith(prefix));

      for (final key in keysToRemove) {
        await remove(key);
      }

      AppLogger.info('Cache nettoyé pour préfixe: $prefix');
    } catch (e) {
      AppLogger.error('Erreur lors du nettoyage du cache $prefix', error: e);
    }
  }

  /// Nettoie tout le cache
  Future<bool> clearAll() async {
    try {
      return await _prefs.clear();
    } catch (e) {
      AppLogger.error('Erreur lors du nettoyage complet du cache', error: e);
      return false;
    }
  }

  /// Nettoie les caches expirés (maintenance)
  Future<void> cleanupExpired() async {
    try {
      final keys = _prefs.getKeys();
      int cleaned = 0;

      for (final key in keys) {
        final jsonString = _prefs.getString(key);
        if (jsonString == null) continue;

        try {
          final cacheData = json.decode(jsonString) as Map<String, dynamic>;
          final timestamp = cacheData['timestamp'] as int;
          final now = DateTime.now().millisecondsSinceEpoch;

          final ttl = _getTTL(key) * 1000;
          if (now - timestamp > ttl) {
            await remove(key);
            cleaned++;
          }
        } catch (_) {
          // Données corrompues, supprimer
          await remove(key);
          cleaned++;
        }
      }

      if (cleaned > 0) {
        AppLogger.info('$cleaned entrées de cache expirées nettoyées');
      }
    } catch (e) {
      AppLogger.error('Erreur lors du nettoyage des caches expirés', error: e);
    }
  }

  /// Récupère le TTL d'une clé en secondes
  int _getTTL(String key) {
    // Chercher une correspondance exacte
    if (_cacheTTL.containsKey(key)) {
      return _cacheTTL[key]!;
    }

    // Chercher par préfixe
    for (final entry in _cacheTTL.entries) {
      if (key.startsWith(entry.key)) {
        return entry.value;
      }
    }

    // TTL par défaut : 5 minutes
    return 300;
  }

  /// Vérifie si une clé existe et n'est pas expirée
  bool has(String key) {
    return get(key) != null;
  }

  /// Retourne des statistiques sur le cache
  Map<String, dynamic> getStats() {
    final keys = _prefs.getKeys();
    int total = keys.length;
    int expired = 0;
    int valid = 0;

    for (final key in keys) {
      if (get(key) == null) {
        expired++;
      } else {
        valid++;
      }
    }

    return {
      'total': total,
      'valid': valid,
      'expired': expired,
    };
  }
}
