import 'package:flutter/material.dart';
import 'package:logger/logger.dart';

import '../../data/repositories/friends_repository_impl.dart';
import '../../data/services/secure_storage.dart';
import '../../domain/entities/friend.dart';
import '../../domain/entities/friend_request.dart';

/// [FriendsProvider] est un `ChangeNotifier` qui gère la logique de gestion des amis.
/// Il interagit avec le [FriendsRepositoryImpl] pour effectuer des appels API et gérer
/// la liste des amis de l'utilisateur, avec une gestion avancée de la pagination,
/// du statut des amis et de la gestion des erreurs.
class FriendsProvider with ChangeNotifier { // Nombre d'amis à récupérer par page

  /// Constructeur de [FriendsProvider] qui nécessite l'instance d'un [FriendsRepositoryImpl].
  FriendsProvider({required this.friendsRepository});
  final FriendsRepositoryImpl friendsRepository;
  final Logger _logger = Logger(); // Utilisation du logger pour une traçabilité complète des actions.

  // Liste des amis
  List<Friend> _friendsList = [];
  bool _isLoading = false; // Indicateur de chargement
  bool _hasMore = true; // Indicateur de pagination
  int _currentPage = 0; // Numéro de la page actuelle pour la pagination
  final int _friendsPerPage = 10;

  // Liste des demandes d'amitié envoyées
  List<FriendRequest> _sentRequests = [];
  bool _isLoadingSentRequests = false;
  int _currentSentRequestPage = 0;

  // Liste des demandes d'amitié reçues
  List<FriendRequest> _receivedRequests = [];
  bool _isLoadingReceivedRequests = false;
  int _currentReceivedRequestPage = 0;

  final int _requestsPerPage = 10;

  // Getters pour accéder à l'état actuel des données
  bool get isLoading => _isLoading;
  bool get hasMore => _hasMore;
  List<Friend> get friendsList => _friendsList;
  List<FriendRequest> get sentRequests => _sentRequests;
  List<FriendRequest> get receivedRequests => _receivedRequests;
  bool get isLoadingSentRequests => _isLoadingSentRequests;
  bool get isLoadingReceivedRequests => _isLoadingReceivedRequests;
  
  // Pour compatibilité avec l'ancien code
  List<FriendRequest> get pendingRequests => _receivedRequests;
  bool get isLoadingRequests => _isLoadingReceivedRequests;

  /// Récupère la liste des amis pour un utilisateur donné avec pagination.
  ///
  /// [userId] : L'identifiant unique de l'utilisateur connecté.
  /// [loadMore] : Si vrai, charge plus d'amis, sinon recharge la liste depuis le début.
  ///
  /// Cette méthode gère :
  /// - La pagination de la liste d'amis.
  /// - L'exclusion de l'utilisateur lui-même.
  /// - Les erreurs et les logs pour une traçabilité complète.
  Future<void> fetchFriends(String userId, {bool loadMore = false}) async {
    if (_isLoading) {
      _logger.w('[LOG] Une opération de chargement est déjà en cours. Annulation de la nouvelle requête.');
      return;
    }

    _isLoading = true;
    notifyListeners();
    _logger.i('[LOG] Début du chargement des amis pour l\'utilisateur $userId.');

    // Réinitialisation de la pagination si ce n'est pas un chargement supplémentaire
    if (!loadMore) {
      _friendsList = [];
      _currentPage = 0;
      _hasMore = true;
      _logger.i('[LOG] Réinitialisation de la pagination et de la liste des amis.');
    }

    try {
      _logger.i('[LOG] Chargement de la page $_currentPage des amis pour l\'utilisateur $userId.');
      final newFriends = await friendsRepository.fetchFriends(userId, _currentPage, _friendsPerPage);

      // Gestion de l'absence de nouveaux amis
      if (newFriends.isEmpty) {
        _hasMore = false;
        _logger.i('[LOG] Plus d\'amis à charger.');
      } else {
        // Ajout des amis à la liste, en excluant l'utilisateur connecté
        for (final friend in newFriends) {
          if (friend.friendId != userId) {
            _friendsList.add(friend);
            _logger.i('[LOG] Ami ajouté : ID = ${friend.friendId}, Nom = ${friend.friendFirstName} ${friend.friendLastName}');
          } else {
            _logger.w("[WARN] L'utilisateur connecté est exclu de la liste des amis : ${friend.friendId}");
          }
        }
        _currentPage++;
        _logger.i('[LOG] Préparation de la page suivante : $_currentPage');
      }
    } catch (e) {
      _logger.e('[ERROR] Erreur lors du chargement des amis : $e');
    } finally {
      _isLoading = false;
      _logger.i('[LOG] Fin du chargement des amis.');
      notifyListeners();
    }
  }

  /// Supprime un ami de la liste locale et de l'API.
  ///
  /// [friendId] : Identifiant unique de l'ami à supprimer.
  ///
  /// Cette méthode :
  /// - Loggue chaque étape.
  /// - Enlève l'ami de la liste locale.
  Future<void> removeFriend(String friendId) async {
    try {
      _logger.i('[LOG] Suppression de l\'ami avec l\'ID : $friendId');
      await friendsRepository.removeFriend(friendId); // Appel API pour supprimer l'ami
      _friendsList.removeWhere((friend) => friend.friendId == friendId); // Suppression locale
      _logger.i('[LOG] Ami supprimé localement avec succès : $friendId');
    } catch (e) {
      _logger.e('[ERROR] Erreur lors de la suppression de l\'ami : $e');
    } finally {
      notifyListeners();
    }
  }

  /// Récupère les détails d'un ami via l'API.
  ///
  /// [userId] : Identifiant de l'utilisateur connecté.
  /// [friendId] : Identifiant de l'ami dont on souhaite récupérer les détails.
  ///
  /// Retourne un `Future<Friend?>` contenant les détails de l'ami ou `null` en cas d'erreur.
  Future<Friend?> fetchFriendDetails(String userId, String friendId) async {
    try {
      _logger.i('[LOG] Récupération des détails de l\'ami avec l\'ID : $friendId');
      final friendDetails = await friendsRepository.getFriendDetails(friendId, userId);

      if (friendDetails != null) {
        _logger.i('[LOG] Détails de l\'ami récupérés avec succès : ${friendDetails.friendId}');
      } else {
        _logger.w('[WARN] Détails de l\'ami introuvables pour l\'ID : $friendId');
      }

      return friendDetails;
    } catch (e) {
      _logger.e('[ERROR] Erreur lors de la récupération des détails de l\'ami : $e');
      return null;
    }
  }

  /// Convertit un statut sous forme de chaîne en [FriendStatus].
  ///
  /// [status] : Le statut sous forme de chaîne (par exemple, 'pending', 'accepted').
  ///
  /// Retourne un [FriendStatus] correspondant, ou `FriendStatus.unknown` si non reconnu.
  FriendStatus _convertToFriendStatus(String status) {
    switch (status.toLowerCase()) {
      case 'pending':
        return FriendStatus.pending;
      case 'accepted':
        return FriendStatus.accepted;
      case 'blocked':
        return FriendStatus.blocked;
      default:
        return FriendStatus.unknown;
    }
  }

  /// Met à jour le statut d'un ami (ex. accepter, bloquer).
  ///
  /// [friendId] : Identifiant de l'ami dont on souhaite mettre à jour le statut.
  /// [status] : Nouveau statut sous forme de chaîne de caractères.
  ///
  /// Loggue l'action, met à jour le statut en local et appelle l'API pour mettre à jour le statut.
  Future<void> updateFriendStatus(String friendId, String status) async {
    try {
      _logger.i('[LOG] Mise à jour du statut de l\'ami avec l\'ID : $friendId');

      // Conversion du statut sous forme de chaîne en statut spécifique
      final friendStatus = _convertToFriendStatus(status);
      await friendsRepository.updateFriendStatus(friendId, status); // Mise à jour dans l'API

      // Mise à jour locale de la liste des amis avec le nouveau statut
      final friendIndex = _friendsList.indexWhere((friend) => friend.friendId == friendId);
      if (friendIndex != -1) {
        _friendsList[friendIndex] = _friendsList[friendIndex].copyWith(status: friendStatus);
        _logger.i('[LOG] Statut de l\'ami mis à jour localement pour l\'ID : $friendId');
      }
    } catch (e) {
      _logger.e('[ERROR] Erreur lors de la mise à jour du statut de l\'ami : $e');
    } finally {
      notifyListeners();
    }
  }

  /// Ajoute un nouvel ami.
  ///
  /// [friendId] : L'identifiant unique de l'ami à ajouter.
  ///
  /// Cette méthode :
  /// - Loggue chaque étape.
  /// - Envoie la demande d'ami via l'API.
  /// - Rafraîchit la liste des amis si l'ajout réussit.
  Future<void> addFriend(String friendId) async {
    try {
      // Récupérer le userId de l'utilisateur actuel
      final currentUserId = await _getCurrentUserId();
      if (currentUserId == null || currentUserId.isEmpty) {
        throw Exception('Utilisateur non connecté');
      }

      _logger.i('[LOG] Ajout de l\'ami: userId=$currentUserId, friendId=$friendId');
      await friendsRepository.addFriend(currentUserId, friendId);
      _logger.i('[LOG] Demande d\'ami envoyée avec succès');
      
      // Rafraîchir la liste des amis après l'ajout
      // Note: L'ami ne sera visible qu'après acceptation de la demande
    } catch (e) {
      _logger.e('[ERROR] Erreur lors de l\'ajout de l\'ami : $e');
      rethrow; // Propager l'erreur pour que l'UI puisse l'afficher
    } finally {
      notifyListeners();
    }
  }

  /// Récupère l'ID de l'utilisateur actuel depuis le stockage sécurisé
  Future<String?> _getCurrentUserId() async {
    try {
      final secureStorage = SecureStorage();
      return await secureStorage.getUserId();
    } catch (e) {
      _logger.e('[ERROR] Erreur lors de la récupération de l\'userId : $e');
      return null;
    }
  }

  /// Récupère les demandes d'amitié en attente pour l'utilisateur actuel (compatibilité).
  Future<void> fetchPendingRequests({bool loadMore = false}) async {
    await fetchReceivedRequests(loadMore: loadMore);
  }

  /// Récupère les demandes d'amitié envoyées par l'utilisateur actuel.
  Future<void> fetchSentRequests({bool loadMore = false}) async {
    try {
      final currentUserId = await _getCurrentUserId();
      if (currentUserId == null || currentUserId.isEmpty) {
        throw Exception('Utilisateur non connecté');
      }

      if (!loadMore) {
        _currentSentRequestPage = 0;
        _sentRequests = [];
      }

      _isLoadingSentRequests = true;
      notifyListeners();

      final page = loadMore ? _currentSentRequestPage + 1 : 0;
      final requests = await friendsRepository.getSentFriendRequests(
        currentUserId,
        page,
        _requestsPerPage,
      );

      if (loadMore) {
        _sentRequests.addAll(requests);
        _currentSentRequestPage = page;
      } else {
        _sentRequests = requests;
        _currentSentRequestPage = 0;
      }

      _logger.i('[LOG] ${requests.length} demandes d\'amitié envoyées récupérées');
    } catch (e) {
      _logger.e('[ERROR] Erreur lors de la récupération des demandes envoyées : $e');
      rethrow;
    } finally {
      _isLoadingSentRequests = false;
      notifyListeners();
    }
  }

  /// Récupère les demandes d'amitié reçues par l'utilisateur actuel.
  Future<void> fetchReceivedRequests({bool loadMore = false}) async {
    try {
      final currentUserId = await _getCurrentUserId();
      if (currentUserId == null || currentUserId.isEmpty) {
        throw Exception('Utilisateur non connecté');
      }

      if (!loadMore) {
        _currentReceivedRequestPage = 0;
        _receivedRequests = [];
      }

      _isLoadingReceivedRequests = true;
      notifyListeners();

      final page = loadMore ? _currentReceivedRequestPage + 1 : 0;
      final requests = await friendsRepository.getReceivedFriendRequests(
        currentUserId,
        page,
        _requestsPerPage,
      );

      if (loadMore) {
        _receivedRequests.addAll(requests);
        _currentReceivedRequestPage = page;
      } else {
        _receivedRequests = requests;
        _currentReceivedRequestPage = 0;
      }

      _logger.i('[LOG] ${requests.length} demandes d\'amitié reçues récupérées');
    } catch (e) {
      _logger.e('[ERROR] Erreur lors de la récupération des demandes reçues : $e');
      rethrow;
    } finally {
      _isLoadingReceivedRequests = false;
      notifyListeners();
    }
  }

  /// Accepte une demande d'amitié.
  Future<void> acceptFriendRequest(String friendshipId) async {
    try {
      _logger.i('[LOG] Acceptation de la demande d\'amitié: $friendshipId');
      await friendsRepository.acceptFriendRequest(friendshipId);
      
      // Retirer la demande de la liste des demandes reçues
      _receivedRequests.removeWhere((req) => req.friendshipId == friendshipId);
      
      // Rafraîchir la liste des amis
      final currentUserId = await _getCurrentUserId();
      if (currentUserId != null) {
        await fetchFriends(currentUserId);
      }
      
      _logger.i('[LOG] Demande d\'amitié acceptée avec succès');
    } catch (e) {
      _logger.e('[ERROR] Erreur lors de l\'acceptation de la demande : $e');
      rethrow;
    } finally {
      notifyListeners();
    }
  }

  /// Rejette une demande d'amitié.
  Future<void> rejectFriendRequest(String friendshipId) async {
    try {
      _logger.i('[LOG] Rejet de la demande d\'amitié: $friendshipId');
      await friendsRepository.rejectFriendRequest(friendshipId);
      
      // Retirer la demande de la liste des demandes reçues
      _receivedRequests.removeWhere((req) => req.friendshipId == friendshipId);
      
      _logger.i('[LOG] Demande d\'amitié rejetée avec succès');
    } catch (e) {
      _logger.e('[ERROR] Erreur lors du rejet de la demande : $e');
      rethrow;
    } finally {
      notifyListeners();
    }
  }

  /// Annule une demande d'amitié envoyée.
  Future<void> cancelFriendRequest(String friendshipId) async {
    try {
      _logger.i('[LOG] Annulation de la demande d\'amitié: $friendshipId');
      await friendsRepository.cancelFriendRequest(friendshipId);
      
      // Retirer la demande de la liste des demandes envoyées
      _sentRequests.removeWhere((req) => req.friendshipId == friendshipId);
      
      _logger.i('[LOG] Demande d\'amitié annulée avec succès');
    } catch (e) {
      _logger.e('[ERROR] Erreur lors de l\'annulation de la demande : $e');
      rethrow;
    } finally {
      notifyListeners();
    }
  }
}
