import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:em2rp/models/event_model.dart';
import 'package:em2rp/services/data_service.dart';
import 'package:em2rp/services/api_service.dart';
import 'package:em2rp/utils/performance_monitor.dart';

class EventProvider with ChangeNotifier {
  final DataService _dataService = DataService(FirebaseFunctionsApiService());
  List<EventModel> _events = [];
  bool _isLoading = false;

  // Cache des utilisateurs chargés depuis getEvents
  Map<String, Map<String, dynamic>> _usersCache = {};

  // Cache pour éviter les rechargements inutiles (ancien système)
  DateTime? _lastLoadTime;
  String? _lastUserId;
  bool _lastCanViewAll = false;

  // Nouveau: Cache par mois pour le lazy loading
  final Map<String, List<EventModel>> _eventsByMonth =
      {}; // "2026-02" => [events]
  String? _currentMonth; // Mois actuellement affiché

  List<EventModel> get events => _events;
  bool get isLoading => _isLoading;

  /// Vérifie si les données doivent être rechargées (cache de 30 secondes)
  bool _shouldReload(String userId, bool canViewAllEvents) {
    if (_lastLoadTime == null) return true;
    if (_lastUserId != userId || _lastCanViewAll != canViewAllEvents)
      return true;

    final now = DateTime.now();
    final difference = now.difference(_lastLoadTime!);
    return difference.inSeconds > 30;
  }

  /// Charger les événements d'un utilisateur via l'API
  Future<void> loadUserEvents(String userId,
      {bool canViewAllEvents = false, bool forceReload = false}) async {
    PerformanceMonitor.start('EventProvider.loadUserEvents');

    // Éviter les rechargements inutiles
    if (!forceReload && !_shouldReload(userId, canViewAllEvents)) {
      print(
          'Using cached events (loaded ${DateTime.now().difference(_lastLoadTime!).inSeconds}s ago)');
      PerformanceMonitor.end('EventProvider.loadUserEvents');
      return;
    }

    _isLoading = true;
    notifyListeners();

    try {
      print(
          'Loading events for user: $userId (canViewAllEvents: $canViewAllEvents)');

      PerformanceMonitor.start('EventProvider.getEvents_API');
      // Charger via l'API - les permissions sont vérifiées côté serveur
      final result = await _dataService.getEvents(userId: userId);
      PerformanceMonitor.end('EventProvider.getEvents_API');

      final eventsData = result['events'] as List<Map<String, dynamic>>;
      final usersData = result['users'] as Map<String, dynamic>;

      // Stocker les utilisateurs dans le cache
      _usersCache = usersData
          .map((key, value) => MapEntry(key, value as Map<String, dynamic>));

      print('Found ${eventsData.length} events from API');

      PerformanceMonitor.start('EventProvider.parseEvents');
      List<EventModel> allEvents = [];
      int failedCount = 0;

      // Parser chaque événement
      for (var eventData in eventsData) {
        try {
          final event =
              EventModel.fromMap(eventData, eventData['id'] as String);
          allEvents.add(event);
        } catch (e) {
          print('Failed to parse event ${eventData['id']}: $e');
          failedCount++;
        }
      }
      PerformanceMonitor.end('EventProvider.parseEvents');

      _events = allEvents;
      _lastLoadTime = DateTime.now();
      _lastUserId = userId;
      _lastCanViewAll = canViewAllEvents;

      print(
          'Successfully loaded ${_events.length} events ($failedCount failed)');

      _isLoading = false;
      notifyListeners();
      PerformanceMonitor.end('EventProvider.loadUserEvents');
    } catch (e) {
      print('Error loading events: $e');
      _isLoading = false;
      notifyListeners();
      PerformanceMonitor.end('EventProvider.loadUserEvents');
      rethrow;
    }
  }

  /// Charger les événements d'un mois spécifique (lazy loading optimisé)
  Future<void> loadMonthEvents(String userId, int year, int month,
      {bool canViewAllEvents = false,
      bool forceReload = false,
      bool silent = false}) async {
    final monthKey = '$year-${month.toString().padLeft(2, '0')}';

    // Vérifier le cache
    if (!forceReload && _eventsByMonth.containsKey(monthKey)) {
      print('[EventProvider] Using cached events for $monthKey');

      if (!silent) {
        _currentMonth = monthKey;
        _events = _eventsByMonth[monthKey]!;
        notifyListeners();
      }
      return;
    }

    if (!silent) {
      _isLoading = true;
      notifyListeners();
    }

    try {
      print('[EventProvider] Loading events for month: $monthKey');

      PerformanceMonitor.start('EventProvider.loadMonthEvents_API');
      final result = await _dataService.getEventsByMonth(
          userId: userId, year: year, month: month);
      PerformanceMonitor.end('EventProvider.loadMonthEvents_API');

      final eventsData = result['events'] as List<Map<String, dynamic>>;
      final usersData = result['users'] as Map<String, dynamic>;

      // Mettre à jour le cache utilisateurs (addAll pour cumuler)
      _usersCache.addAll(usersData
          .map((key, value) => MapEntry(key, value as Map<String, dynamic>)));

      print('[EventProvider] Found ${eventsData.length} events for $monthKey');

      PerformanceMonitor.start('EventProvider.parseMonthEvents');
      List<EventModel> monthEvents = [];
      int failedCount = 0;

      // Parser les événements
      for (var eventData in eventsData) {
        try {
          final event =
              EventModel.fromMap(eventData, eventData['id'] as String);
          monthEvents.add(event);
        } catch (e) {
          print('[EventProvider] Failed to parse event ${eventData['id']}: $e');
          failedCount++;
        }
      }
      PerformanceMonitor.end('EventProvider.parseMonthEvents');

      // Stocker dans le cache par mois
      _eventsByMonth[monthKey] = monthEvents;

      // Mettre à jour _events et _currentMonth seulement si ce n'est pas un préchargement silencieux
      if (!silent) {
        _currentMonth = monthKey;
        _events = monthEvents;
      }

      // Mettre à jour les infos de cache global
      _lastLoadTime = DateTime.now();
      _lastUserId = userId;
      _lastCanViewAll = canViewAllEvents;

      print(
          '[EventProvider] Successfully loaded ${monthEvents.length} events for $monthKey ($failedCount failed)');

      if (!silent) {
        _isLoading = false;
        notifyListeners();
      }
    } catch (e) {
      print('[EventProvider] Error loading month events: $e');
      if (!silent) {
        _isLoading = false;
        notifyListeners();
      }
      rethrow;
    }
  }

  /// Précharger les mois adjacents en arrière-plan
  void preloadAdjacentMonths(String userId, int year, int month,
      {bool canViewAllEvents = false}) {
    // Mois précédent
    final prevMonth = month == 1 ? 12 : month - 1;
    final prevYear = month == 1 ? year - 1 : year;

    // Mois suivant
    final nextMonth = month == 12 ? 1 : month + 1;
    final nextYear = month == 12 ? year + 1 : year;

    print('[EventProvider] Preloading adjacent months...');

    // Charger en arrière-plan (sans bloquer l'UI ni notifier)
    Future.microtask(() async {
      try {
        await loadMonthEvents(userId, prevYear, prevMonth,
            canViewAllEvents: canViewAllEvents, silent: true);
        await loadMonthEvents(userId, nextYear, nextMonth,
            canViewAllEvents: canViewAllEvents, silent: true);
        print('[EventProvider] Adjacent months preloaded successfully');
      } catch (e) {
        print('[EventProvider] Error preloading adjacent months: $e');
      }
    });
  }

  /// Vide entièrement le cache (mois + métadonnées) pour forcer un rechargement complet
  void clearAllCache() {
    _eventsByMonth.clear();
    _lastLoadTime = null;
    _lastUserId = null;
    _currentMonth = null;
    print('[EventProvider] Cache entièrement vidé');
  }

  /// Recharger les événements (utilise le dernier userId)
  Future<void> refreshEvents(String userId,
      {bool canViewAllEvents = false}) async {
    await loadUserEvents(userId,
        canViewAllEvents: canViewAllEvents, forceReload: true);
  }

  /// Récupérer un événement spécifique par ID
  EventModel? getEventById(String eventId) {
    try {
      return _events.firstWhere((event) => event.id == eventId);
    } catch (e) {
      return null;
    }
  }

  /// Recherche des événements accessibles à l'utilisateur.
  Future<List<EventModel>> searchEvents({
    required String userId,
    required String query,
    int limit = 20,
  }) async {
    final trimmedQuery = query.trim();
    if (trimmedQuery.isEmpty) {
      return [];
    }

    final result = await _dataService.searchEvents(
      userId: userId,
      query: trimmedQuery,
      limit: limit,
    );

    final events = <EventModel>[];
    for (final eventData in result) {
      try {
        final eventId = eventData['id'] as String?;
        if (eventId == null || eventId.isEmpty) {
          continue;
        }

        events.add(EventModel.fromMap(eventData, eventId));
      } catch (e) {
        print('Failed to parse searched event ${eventData['id']}: $e');
      }
    }

    events.sort((a, b) => a.startDateTime.compareTo(b.startDateTime));
    return events;
  }

  /// Ajouter un nouvel événement
  Future<void> addEvent(EventModel event) async {
    try {
      final monthKey =
          '${event.startDateTime.year}-${event.startDateTime.month.toString().padLeft(2, '0')}';
          
      // Ajouter dans le cache par mois s'il est chargé
      if (_eventsByMonth.containsKey(monthKey)) {
        if (!_eventsByMonth[monthKey]!.any((e) => e.id == event.id)) {
          _eventsByMonth[monthKey]!.add(event);
        }
      }

      // Si _events est la même instance de liste, il a déjà été ajouté.
      // Sinon (par exemple si on affiche un autre mois ou cache désynchronisé),
      // on l'ajoute à _events uniquement si c'est pour le mois courant.
      if ((_currentMonth == null || _currentMonth == monthKey) && !_events.any((e) => e.id == event.id)) {
        _events.add(event);
      }

      notifyListeners();
    } catch (e) {
      print('Error adding event: $e');
      rethrow;
    }
  }

  /// Mettre à jour un événement
  Future<void> updateEvent(EventModel event) async {
    try {
      // Mise à jour dans _events
      final index = _events.indexWhere((e) => e.id == event.id);
      if (index != -1) {
        final oldEvent = _events[index];
        _events[index] = event;

        // Mettre à jour dans le cache par mois
        final oldMonthKey =
            '${oldEvent.startDateTime.year}-${oldEvent.startDateTime.month.toString().padLeft(2, '0')}';
        final newMonthKey =
            '${event.startDateTime.year}-${event.startDateTime.month.toString().padLeft(2, '0')}';

        // Si le mois a changé, supprimer de l'ancien et ajouter au nouveau
        if (oldMonthKey != newMonthKey) {
          if (_eventsByMonth.containsKey(oldMonthKey)) {
            _eventsByMonth[oldMonthKey]!.removeWhere((e) => e.id == event.id);
          }
          if (_eventsByMonth.containsKey(newMonthKey)) {
            _eventsByMonth[newMonthKey]!.add(event);
          }
        } else {
          // Même mois, juste mettre à jour
          if (_eventsByMonth.containsKey(newMonthKey)) {
            final monthIndex = _eventsByMonth[newMonthKey]!
                .indexWhere((e) => e.id == event.id);
            if (monthIndex != -1) {
              _eventsByMonth[newMonthKey]![monthIndex] = event;
            }
          }
        }

        notifyListeners();
      }
    } catch (e) {
      print('Error updating event: $e');
      rethrow;
    }
  }

  /// Supprimer un événement
  Future<void> deleteEvent(String eventId) async {
    try {
      await _dataService.deleteEvent(eventId);

      // Trouver l'événement pour obtenir sa date avant de le supprimer
      final eventToDelete = _events.firstWhere((e) => e.id == eventId);
      final monthKey =
          '${eventToDelete.startDateTime.year}-${eventToDelete.startDateTime.month.toString().padLeft(2, '0')}';

      // Supprimer de _events
      _events.removeWhere((event) => event.id == eventId);

      // Supprimer du cache par mois
      if (_eventsByMonth.containsKey(monthKey)) {
        _eventsByMonth[monthKey]!.removeWhere((event) => event.id == eventId);
      }

      notifyListeners();
    } catch (e) {
      print('Error deleting event: $e');
      rethrow;
    }
  }

  /// Récupérer les données d'un utilisateur depuis le cache
  Map<String, dynamic>? getUserFromCache(String uid) {
    return _usersCache[uid];
  }

  /// Récupérer les utilisateurs de la workforce d'un événement
  List<Map<String, dynamic>> getWorkforceUsers(EventModel event) {
    final users = <Map<String, dynamic>>[];

    for (final dynamic userRef in event.workforce) {
      try {
        String? uid;

        // Tenter d'extraire l'UID
        if (userRef is String) {
          uid = userRef;
        } else {
          // Essayer d'extraire l'ID si c'est une DocumentReference
          final ref = userRef as DocumentReference?;
          uid = ref?.id;
        }

        if (uid != null) {
          final userData = getUserFromCache(uid);
          if (userData != null) {
            users.add(userData);
          }
        }
      } catch (e) {
        // Ignorer les références invalides
        print('Skipping invalid workforce reference: $userRef');
      }
    }

    return users;
  }

  /// Vider la liste des événements
  void clearEvents() {
    _events = [];
    _lastLoadTime = null;
    _lastUserId = null;
    _lastCanViewAll = false;
    notifyListeners();
  }
}
