/// BLoC pour la gestion des membres
library membres_bloc;

import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:dio/dio.dart';
import 'package:injectable/injectable.dart';
import 'membres_event.dart';
import 'membres_state.dart';
import '../domain/usecases/get_members.dart';
import '../domain/usecases/get_member_by_id.dart';
import '../domain/usecases/create_member.dart' as uc;
import '../domain/usecases/update_member.dart' as uc;
import '../domain/usecases/delete_member.dart' as uc;
import '../domain/usecases/search_members.dart';
import '../domain/usecases/get_member_stats.dart';
import '../domain/repositories/membre_repository.dart';

/// BLoC pour la gestion des membres (Clean Architecture)
@injectable
class MembresBloc extends Bloc<MembresEvent, MembresState> {
  final GetMembers _getMembers;
  final GetMemberById _getMemberById;
  final uc.CreateMember _createMember;
  final uc.UpdateMember _updateMember;
  final uc.DeleteMember _deleteMember;
  final SearchMembers _searchMembers;
  final GetMemberStats _getMemberStats;
  final IMembreRepository _repository; // Pour méthodes non-couvertes par use cases

  MembresBloc(
    this._getMembers,
    this._getMemberById,
    this._createMember,
    this._updateMember,
    this._deleteMember,
    this._searchMembers,
    this._getMemberStats,
    this._repository,
  ) : super(const MembresInitial()) {
    on<LoadMembres>(_onLoadMembres);
    on<LoadMembreById>(_onLoadMembreById);
    on<CreateMembre>(_onCreateMembre);
    on<UpdateMembre>(_onUpdateMembre);
    on<DeleteMembre>(_onDeleteMembre);
    on<ActivateMembre>(_onActivateMembre);
    on<DeactivateMembre>(_onDeactivateMembre);
    on<SearchMembres>(_onSearchMembres);
    on<LoadActiveMembres>(_onLoadActiveMembres);
    on<LoadBureauMembres>(_onLoadBureauMembres);
    on<LoadMembresStats>(_onLoadMembresStats);
  }

  /// Charge la liste des membres
  Future<void> _onLoadMembres(
    LoadMembres event,
    Emitter<MembresState> emit,
  ) async {
    try {
      // Si refresh et qu'on a déjà des données, on garde l'état actuel
      if (event.refresh && state is MembresLoaded) {
        final currentState = state as MembresLoaded;
        emit(MembresRefreshing(currentState.membres));
      } else {
        emit(const MembresLoading());
      }

      final result = await _getMembers(
        page: event.page,
        size: event.size,
        recherche: event.recherche,
      );

      emit(MembresLoaded(
        membres: result.membres,
        totalElements: result.totalElements,
        currentPage: result.currentPage,
        pageSize: result.pageSize,
        totalPages: result.totalPages,
      ));
    } on DioException catch (e) {
      emit(MembresNetworkError(
        message: _getNetworkErrorMessage(e),
        code: e.response?.statusCode.toString(),
        error: e,
      ));
    } catch (e) {
      emit(MembresError(
        message: 'Erreur inattendue lors du chargement des membres: $e',
        error: e,
      ));
    }
  }

  /// Charge un membre par ID
  Future<void> _onLoadMembreById(
    LoadMembreById event,
    Emitter<MembresState> emit,
  ) async {
    try {
      emit(const MembresLoading());

      final membre = await _getMemberById(event.id);

      if (membre != null) {
        emit(MembreDetailLoaded(membre));
      } else {
        emit(const MembresError(
          message: 'Membre non trouvé',
          code: '404',
        ));
      }
    } on DioException catch (e) {
      emit(MembresNetworkError(
        message: _getNetworkErrorMessage(e),
        code: e.response?.statusCode.toString(),
        error: e,
      ));
    } catch (e) {
      emit(MembresError(
        message: 'Erreur lors du chargement du membre: $e',
        error: e,
      ));
    }
  }

  /// Crée un nouveau membre
  Future<void> _onCreateMembre(
    CreateMembre event,
    Emitter<MembresState> emit,
  ) async {
    try {
      emit(const MembresLoading());

      final membre = await _createMember(event.membre);

      emit(MembreCreated(membre));
    } on DioException catch (e) {
      if (e.response?.statusCode == 400) {
        // Erreur de validation
        final errors = _extractValidationErrors(e.response?.data);
        emit(MembresValidationError(
          message: 'Erreur de validation',
          validationErrors: errors,
          code: '400',
        ));
      } else {
        emit(MembresNetworkError(
          message: _getNetworkErrorMessage(e),
          code: e.response?.statusCode.toString(),
          error: e,
        ));
      }
    } catch (e) {
      emit(MembresError(
        message: 'Erreur lors de la création du membre: $e',
        error: e,
      ));
    }
  }

  /// Met à jour un membre
  Future<void> _onUpdateMembre(
    UpdateMembre event,
    Emitter<MembresState> emit,
  ) async {
    try {
      emit(const MembresLoading());

      final membre = await _updateMember(event.id, event.membre);

      emit(MembreUpdated(membre));
    } on DioException catch (e) {
      if (e.response?.statusCode == 400) {
        final errors = _extractValidationErrors(e.response?.data);
        emit(MembresValidationError(
          message: 'Erreur de validation',
          validationErrors: errors,
          code: '400',
        ));
      } else {
        emit(MembresNetworkError(
          message: _getNetworkErrorMessage(e),
          code: e.response?.statusCode.toString(),
          error: e,
        ));
      }
    } catch (e) {
      emit(MembresError(
        message: 'Erreur lors de la mise à jour du membre: $e',
        error: e,
      ));
    }
  }

  /// Supprime un membre
  Future<void> _onDeleteMembre(
    DeleteMembre event,
    Emitter<MembresState> emit,
  ) async {
    try {
      emit(const MembresLoading());

      await _deleteMember(event.id);

      emit(MembreDeleted(event.id));
    } on DioException catch (e) {
      emit(MembresNetworkError(
        message: _getNetworkErrorMessage(e),
        code: e.response?.statusCode.toString(),
        error: e,
      ));
    } catch (e) {
      emit(MembresError(
        message: 'Erreur lors de la suppression du membre: $e',
        error: e,
      ));
    }
  }

  /// Active un membre
  Future<void> _onActivateMembre(
    ActivateMembre event,
    Emitter<MembresState> emit,
  ) async {
    try {
      emit(const MembresLoading());

      final membre = await _repository.activateMembre(event.id);

      emit(MembreActivated(membre));
    } on DioException catch (e) {
      emit(MembresNetworkError(
        message: _getNetworkErrorMessage(e),
        code: e.response?.statusCode.toString(),
        error: e,
      ));
    } catch (e) {
      emit(MembresError(
        message: 'Erreur lors de l\'activation du membre: $e',
        error: e,
      ));
    }
  }

  /// Désactive un membre
  Future<void> _onDeactivateMembre(
    DeactivateMembre event,
    Emitter<MembresState> emit,
  ) async {
    try {
      emit(const MembresLoading());

      final membre = await _repository.deactivateMembre(event.id);

      emit(MembreDeactivated(membre));
    } on DioException catch (e) {
      emit(MembresNetworkError(
        message: _getNetworkErrorMessage(e),
        code: e.response?.statusCode.toString(),
        error: e,
      ));
    } catch (e) {
      emit(MembresError(
        message: 'Erreur lors de la désactivation du membre: $e',
        error: e,
      ));
    }
  }

  /// Recherche avancée de membres
  Future<void> _onSearchMembres(
    SearchMembres event,
    Emitter<MembresState> emit,
  ) async {
    try {
      emit(const MembresLoading());

      final result = await _searchMembers(
        criteria: event.criteria,
        page: event.page,
        size: event.size,
      );

      emit(MembresLoaded(
        membres: result.membres,
        totalElements: result.totalElements,
        currentPage: result.currentPage,
        pageSize: result.pageSize,
        totalPages: result.totalPages,
      ));
    } on DioException catch (e) {
      emit(MembresNetworkError(
        message: _getNetworkErrorMessage(e),
        code: e.response?.statusCode.toString(),
        error: e,
      ));
    } catch (e) {
      emit(MembresError(
        message: 'Erreur lors de la recherche de membres: $e',
        error: e,
      ));
    }
  }

  /// Charge les membres actifs
  Future<void> _onLoadActiveMembres(
    LoadActiveMembres event,
    Emitter<MembresState> emit,
  ) async {
    try {
      emit(const MembresLoading());

      final result = await _repository.getActiveMembers(
        page: event.page,
        size: event.size,
      );

      emit(MembresLoaded(
        membres: result.membres,
        totalElements: result.totalElements,
        currentPage: result.currentPage,
        pageSize: result.pageSize,
        totalPages: result.totalPages,
      ));
    } on DioException catch (e) {
      emit(MembresNetworkError(
        message: _getNetworkErrorMessage(e),
        code: e.response?.statusCode.toString(),
        error: e,
      ));
    } catch (e) {
      emit(MembresError(
        message: 'Erreur lors du chargement des membres actifs: $e',
        error: e,
      ));
    }
  }

  /// Charge les membres du bureau
  Future<void> _onLoadBureauMembres(
    LoadBureauMembres event,
    Emitter<MembresState> emit,
  ) async {
    try {
      emit(const MembresLoading());

      final result = await _repository.getBureauMembers(
        page: event.page,
        size: event.size,
      );

      emit(MembresLoaded(
        membres: result.membres,
        totalElements: result.totalElements,
        currentPage: result.currentPage,
        pageSize: result.pageSize,
        totalPages: result.totalPages,
      ));
    } on DioException catch (e) {
      emit(MembresNetworkError(
        message: _getNetworkErrorMessage(e),
        code: e.response?.statusCode.toString(),
        error: e,
      ));
    } catch (e) {
      emit(MembresError(
        message: 'Erreur lors du chargement des membres du bureau: $e',
        error: e,
      ));
    }
  }

  /// Charge les statistiques
  Future<void> _onLoadMembresStats(
    LoadMembresStats event,
    Emitter<MembresState> emit,
  ) async {
    try {
      emit(const MembresLoading());

      final stats = await _getMemberStats();

      emit(MembresStatsLoaded(stats));
    } on DioException catch (e) {
      emit(MembresNetworkError(
        message: _getNetworkErrorMessage(e),
        code: e.response?.statusCode.toString(),
        error: e,
      ));
    } catch (e) {
      emit(MembresError(
        message: 'Erreur lors du chargement des statistiques: $e',
        error: e,
      ));
    }
  }

  /// Extrait les erreurs de validation de la réponse
  Map<String, String> _extractValidationErrors(dynamic data) {
    final errors = <String, String>{};
    if (data is Map<String, dynamic> && data.containsKey('errors')) {
      final errorsData = data['errors'];
      if (errorsData is Map<String, dynamic>) {
        errorsData.forEach((key, value) {
          errors[key] = value.toString();
        });
      }
    }
    return errors;
  }

  /// Génère un message d'erreur réseau approprié
  String _getNetworkErrorMessage(DioException e) {
    switch (e.type) {
      case DioExceptionType.connectionTimeout:
        return 'Délai de connexion dépassé. Vérifiez votre connexion internet.';
      case DioExceptionType.sendTimeout:
        return 'Délai d\'envoi dépassé. Vérifiez votre connexion internet.';
      case DioExceptionType.receiveTimeout:
        return 'Délai de réception dépassé. Vérifiez votre connexion internet.';
      case DioExceptionType.badResponse:
        final statusCode = e.response?.statusCode;
        if (statusCode == 401) {
          return 'Non autorisé. Veuillez vous reconnecter.';
        } else if (statusCode == 403) {
          return 'Accès refusé. Vous n\'avez pas les permissions nécessaires.';
        } else if (statusCode == 404) {
          return 'Ressource non trouvée.';
        } else if (statusCode == 409) {
          return 'Conflit. Cette ressource existe déjà.';
        } else if (statusCode != null && statusCode >= 500) {
          return 'Erreur serveur. Veuillez réessayer plus tard.';
        }
        return 'Erreur lors de la communication avec le serveur.';
      case DioExceptionType.cancel:
        return 'Requête annulée.';
      case DioExceptionType.unknown:
        return 'Erreur de connexion. Vérifiez votre connexion internet.';
      default:
        return 'Erreur réseau inattendue.';
    }
  }
}

