/// Moteur de permissions ultra-performant avec cache intelligent
/// Vérifications contextuelles et audit trail intégré
library permission_engine;

import 'dart:async';
import 'package:flutter/foundation.dart';
import '../models/user.dart';
import '../models/user_role.dart';
import '../models/permission_matrix.dart';

/// Moteur de permissions haute performance avec cache multi-niveaux
/// 
/// Fonctionnalités :
/// - Cache mémoire ultra-rapide avec TTL
/// - Vérifications contextuelles avancées
/// - Audit trail automatique
/// - Support des permissions héritées
/// - Invalidation intelligente du cache
class PermissionEngine {
  static final PermissionEngine _instance = PermissionEngine._internal();
  factory PermissionEngine() => _instance;
  PermissionEngine._internal();

  /// Cache mémoire des permissions avec TTL
  static final Map<String, _CachedPermission> _permissionCache = {};
  
  /// Cache des permissions effectives par utilisateur
  static final Map<String, _CachedUserPermissions> _userPermissionsCache = {};
  
  /// Durée de vie du cache (5 minutes par défaut)
  static const Duration _defaultCacheTTL = Duration(minutes: 5);
  
  /// Durée de vie du cache pour les super admins (plus long)
  static const Duration _superAdminCacheTTL = Duration(minutes: 15);
  
  /// Compteur de hits/miss du cache pour monitoring
  static int _cacheHits = 0;
  static int _cacheMisses = 0;
  
  /// Stream pour les événements d'audit
  static final StreamController<PermissionAuditEvent> _auditController = 
      StreamController<PermissionAuditEvent>.broadcast();
  
  /// Stream des événements d'audit
  static Stream<PermissionAuditEvent> get auditStream => _auditController.stream;

  /// Vérifie si un utilisateur a une permission spécifique
  /// 
  /// [user] - Utilisateur à vérifier
  /// [permission] - Permission à vérifier
  /// [organizationId] - Contexte organisationnel optionnel
  /// [auditLog] - Activer l'audit trail (défaut: true)
  static Future<bool> hasPermission(
    User user,
    String permission, {
    String? organizationId,
    bool auditLog = true,
  }) async {
    final cacheKey = _generateCacheKey(user.id, permission, organizationId);
    
    // Vérification du cache
    final cachedResult = _getCachedPermission(cacheKey);
    if (cachedResult != null) {
      _cacheHits++;
      if (auditLog && !cachedResult.result) {
        _logAuditEvent(user, permission, false, 'CACHED_DENIED', organizationId);
      }
      return cachedResult.result;
    }
    
    _cacheMisses++;
    
    // Calcul de la permission
    final result = await _computePermission(user, permission, organizationId);
    
    // Mise en cache
    _cachePermission(cacheKey, result, user.primaryRole);
    
    // Audit trail
    if (auditLog) {
      _logAuditEvent(
        user, 
        permission, 
        result, 
        result ? 'GRANTED' : 'DENIED',
        organizationId,
      );
    }
    
    return result;
  }

  /// Vérifie plusieurs permissions en une seule fois
  static Future<Map<String, bool>> hasPermissions(
    User user,
    List<String> permissions, {
    String? organizationId,
    bool auditLog = true,
  }) async {
    final results = <String, bool>{};
    
    // Traitement en parallèle pour les performances
    final futures = permissions.map((permission) => 
      hasPermission(user, permission, organizationId: organizationId, auditLog: auditLog)
        .then((result) => MapEntry(permission, result))
    );
    
    final entries = await Future.wait(futures);
    for (final entry in entries) {
      results[entry.key] = entry.value;
    }
    
    return results;
  }

  /// Obtient toutes les permissions effectives d'un utilisateur
  static Future<List<String>> getEffectivePermissions(
    User user, {
    String? organizationId,
  }) async {
    final cacheKey = '${user.id}_effective_${organizationId ?? 'global'}';
    
    // Vérification du cache utilisateur
    final cachedUserPermissions = _getCachedUserPermissions(cacheKey);
    if (cachedUserPermissions != null) {
      _cacheHits++;
      return cachedUserPermissions.permissions;
    }
    
    _cacheMisses++;
    
    // Calcul des permissions effectives
    final permissions = user.getEffectivePermissions(organizationId: organizationId);
    
    // Mise en cache
    _cacheUserPermissions(cacheKey, permissions, user.primaryRole);
    
    return permissions;
  }

  /// Vérifie si un utilisateur peut effectuer une action sur un domaine
  static Future<bool> canPerformAction(
    User user,
    String domain,
    String action, {
    String scope = 'own',
    String? organizationId,
  }) async {
    final permission = '$domain.$action.$scope';
    return hasPermission(user, permission, organizationId: organizationId);
  }

  /// Invalide le cache pour un utilisateur spécifique
  static void invalidateUserCache(String userId) {
    final keysToRemove = <String>[];
    
    // Invalider le cache des permissions
    for (final key in _permissionCache.keys) {
      if (key.startsWith('${userId}_')) {
        keysToRemove.add(key);
      }
    }
    
    for (final key in keysToRemove) {
      _permissionCache.remove(key);
    }
    
    // Invalider le cache des permissions utilisateur
    final userKeysToRemove = <String>[];
    for (final key in _userPermissionsCache.keys) {
      if (key.startsWith('${userId}_')) {
        userKeysToRemove.add(key);
      }
    }
    
    for (final key in userKeysToRemove) {
      _userPermissionsCache.remove(key);
    }
    
    debugPrint('Cache invalidé pour l\'utilisateur: $userId');
  }

  /// Invalide tout le cache
  static void invalidateAllCache() {
    _permissionCache.clear();
    _userPermissionsCache.clear();
    debugPrint('Cache complet invalidé');
  }

  /// Obtient les statistiques du cache
  static Map<String, dynamic> getCacheStats() {
    final totalRequests = _cacheHits + _cacheMisses;
    final hitRate = totalRequests > 0 ? (_cacheHits / totalRequests * 100) : 0.0;
    
    return {
      'cacheHits': _cacheHits,
      'cacheMisses': _cacheMisses,
      'hitRate': hitRate.toStringAsFixed(2),
      'permissionCacheSize': _permissionCache.length,
      'userPermissionsCacheSize': _userPermissionsCache.length,
    };
  }

  /// Nettoie le cache expiré
  static void cleanExpiredCache() {
    final now = DateTime.now();
    
    // Nettoyer le cache des permissions
    _permissionCache.removeWhere((key, cached) => cached.expiresAt.isBefore(now));
    
    // Nettoyer le cache des permissions utilisateur
    _userPermissionsCache.removeWhere((key, cached) => cached.expiresAt.isBefore(now));
    
    debugPrint('Cache expiré nettoyé');
  }

  // === MÉTHODES PRIVÉES ===

  /// Calcule une permission sans cache
  static Future<bool> _computePermission(
    User user,
    String permission,
    String? organizationId,
  ) async {
    // Vérification des permissions publiques
    if (PermissionMatrix.isPublicPermission(permission)) {
      return true;
    }
    
    // Vérification utilisateur actif
    if (!user.isActive) return false;
    
    // Vérification directe de l'utilisateur
    if (user.hasPermission(permission, organizationId: organizationId)) {
      return true;
    }
    
    // Vérifications contextuelles avancées
    return _checkContextualPermissions(user, permission, organizationId);
  }

  /// Vérifications contextuelles avancées (intégration serveur).
  /// Quand le backend exposera GET /api/permissions/check avec userId, permission, organizationId,
  /// remplacer le return false par l'appel API et le résultat.
  static Future<bool> _checkContextualPermissions(
    User user,
    String permission,
    String? organizationId,
  ) async {
    // Vérification contextuelle désactivée — endpoint non disponible.
    return false;
  }

  /// Génère une clé de cache unique
  static String _generateCacheKey(String userId, String permission, String? organizationId) {
    return '${userId}_${permission}_${organizationId ?? 'global'}';
  }

  /// Obtient une permission depuis le cache
  static _CachedPermission? _getCachedPermission(String key) {
    final cached = _permissionCache[key];
    if (cached != null && cached.expiresAt.isAfter(DateTime.now())) {
      return cached;
    }
    
    if (cached != null) {
      _permissionCache.remove(key);
    }
    
    return null;
  }

  /// Met en cache une permission
  static void _cachePermission(String key, bool result, UserRole userRole) {
    final ttl = userRole == UserRole.superAdmin ? _superAdminCacheTTL : _defaultCacheTTL;
    
    _permissionCache[key] = _CachedPermission(
      result: result,
      expiresAt: DateTime.now().add(ttl),
    );
  }

  /// Obtient les permissions utilisateur depuis le cache
  static _CachedUserPermissions? _getCachedUserPermissions(String key) {
    final cached = _userPermissionsCache[key];
    if (cached != null && cached.expiresAt.isAfter(DateTime.now())) {
      return cached;
    }
    
    if (cached != null) {
      _userPermissionsCache.remove(key);
    }
    
    return null;
  }

  /// Met en cache les permissions utilisateur
  static void _cacheUserPermissions(String key, List<String> permissions, UserRole userRole) {
    final ttl = userRole == UserRole.superAdmin ? _superAdminCacheTTL : _defaultCacheTTL;
    
    _userPermissionsCache[key] = _CachedUserPermissions(
      permissions: permissions,
      expiresAt: DateTime.now().add(ttl),
    );
  }

  /// Enregistre un événement d'audit
  static void _logAuditEvent(
    User user,
    String permission,
    bool granted,
    String reason,
    String? organizationId,
  ) {
    final event = PermissionAuditEvent(
      userId: user.id,
      userEmail: user.email,
      permission: permission,
      granted: granted,
      reason: reason,
      organizationId: organizationId,
      timestamp: DateTime.now(),
    );
    
    _auditController.add(event);
  }
}

/// Classe pour les permissions mises en cache
class _CachedPermission {
  final bool result;
  final DateTime expiresAt;
  
  _CachedPermission({required this.result, required this.expiresAt});
}

/// Classe pour les permissions utilisateur mises en cache
class _CachedUserPermissions {
  final List<String> permissions;
  final DateTime expiresAt;
  
  _CachedUserPermissions({required this.permissions, required this.expiresAt});
}

/// Événement d'audit des permissions
class PermissionAuditEvent {
  final String userId;
  final String userEmail;
  final String permission;
  final bool granted;
  final String reason;
  final String? organizationId;
  final DateTime timestamp;
  
  PermissionAuditEvent({
    required this.userId,
    required this.userEmail,
    required this.permission,
    required this.granted,
    required this.reason,
    this.organizationId,
    required this.timestamp,
  });
  
  Map<String, dynamic> toJson() {
    return {
      'userId': userId,
      'userEmail': userEmail,
      'permission': permission,
      'granted': granted,
      'reason': reason,
      'organizationId': organizationId,
      'timestamp': timestamp.toIso8601String(),
    };
  }
}
