/// BLoC pour la gestion des événements
library evenements_bloc;

import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:dio/dio.dart';
import 'package:injectable/injectable.dart';
import 'evenements_event.dart';
import 'evenements_state.dart';
import '../domain/usecases/get_events.dart';
import '../domain/usecases/get_event_by_id.dart';
import '../domain/usecases/create_event.dart' as uc;
import '../domain/usecases/update_event.dart' as uc;
import '../domain/usecases/delete_event.dart' as uc;
import '../domain/usecases/register_for_event.dart';
import '../domain/usecases/cancel_registration.dart';
import '../domain/usecases/get_my_registrations.dart';
import '../domain/usecases/get_event_participants.dart';
import '../domain/repositories/evenement_repository.dart';

/// BLoC pour la gestion des événements (Clean Architecture)
@injectable
class EvenementsBloc extends Bloc<EvenementsEvent, EvenementsState> {
  final GetEvents _getEvents;
  final GetEventById _getEventById;
  final uc.CreateEvent _createEvent;
  final uc.UpdateEvent _updateEvent;
  final uc.DeleteEvent _deleteEvent;
  final RegisterForEvent _registerForEvent;
  final CancelRegistration _cancelRegistration;
  final GetMyRegistrations _getMyRegistrations;
  final GetEventParticipants _getEventParticipants;
  final IEvenementRepository _repository; // Pour méthodes non-couvertes par use cases

  EvenementsBloc(
    this._getEvents,
    this._getEventById,
    this._createEvent,
    this._updateEvent,
    this._deleteEvent,
    this._registerForEvent,
    this._cancelRegistration,
    this._getMyRegistrations,
    this._getEventParticipants,
    this._repository,
  ) : super(const EvenementsInitial()) {
    on<LoadEvenements>(_onLoadEvenements);
    on<LoadEvenementById>(_onLoadEvenementById);
    on<CreateEvenement>(_onCreateEvenement);
    on<UpdateEvenement>(_onUpdateEvenement);
    on<DeleteEvenement>(_onDeleteEvenement);
    on<LoadEvenementsAVenir>(_onLoadEvenementsAVenir);
    on<LoadEvenementsEnCours>(_onLoadEvenementsEnCours);
    on<LoadEvenementsPasses>(_onLoadEvenementsPasses);
    on<InscrireEvenement>(_onInscrireEvenement);
    on<DesinscrireEvenement>(_onDesinscrireEvenement);
    on<LoadParticipants>(_onLoadParticipants);
    on<LoadEvenementsStats>(_onLoadEvenementsStats);
  }

  /// Charge la liste des événements
  Future<void> _onLoadEvenements(
    LoadEvenements event,
    Emitter<EvenementsState> emit,
  ) async {
    try {
      if (event.refresh && state is EvenementsLoaded) {
        final currentState = state as EvenementsLoaded;
        emit(EvenementsRefreshing(currentState.evenements));
      } else {
        emit(const EvenementsLoading());
      }

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

      emit(EvenementsLoaded(
        evenements: result.evenements,
        total: result.total,
        page: result.page,
        size: result.size,
        totalPages: result.totalPages,
      ));
    } on DioException catch (e) {
      emit(EvenementsNetworkError(
        message: _getNetworkErrorMessage(e),
        code: e.response?.statusCode.toString(),
        error: e,
      ));
    } catch (e) {
      emit(EvenementsError(
        message: 'Erreur inattendue lors du chargement des événements: $e',
        error: e,
      ));
    }
  }

  /// Charge un événement par ID
  Future<void> _onLoadEvenementById(
    LoadEvenementById event,
    Emitter<EvenementsState> emit,
  ) async {
    try {
      emit(const EvenementsLoading());

      final evenement = await _getEventById(event.id);

      if (evenement != null) {
        emit(EvenementDetailLoaded(evenement));
      } else {
        emit(const EvenementsError(
          message: 'Événement non trouvé',
          code: '404',
        ));
      }
    } on DioException catch (e) {
      emit(EvenementsNetworkError(
        message: _getNetworkErrorMessage(e),
        code: e.response?.statusCode.toString(),
        error: e,
      ));
    } catch (e) {
      emit(EvenementsError(
        message: 'Erreur lors du chargement de l\'événement: $e',
        error: e,
      ));
    }
  }

  /// Crée un nouvel événement
  Future<void> _onCreateEvenement(
    CreateEvenement event,
    Emitter<EvenementsState> emit,
  ) async {
    try {
      emit(const EvenementsLoading());

      final evenement = await _createEvent(event.evenement);

      emit(EvenementCreated(evenement));
    } on DioException catch (e) {
      if (e.response?.statusCode == 400) {
        final errors = _extractValidationErrors(e.response?.data);
        emit(EvenementsValidationError(
          message: 'Erreur de validation',
          validationErrors: errors,
          code: '400',
        ));
      } else {
        emit(EvenementsNetworkError(
          message: _getNetworkErrorMessage(e),
          code: e.response?.statusCode.toString(),
          error: e,
        ));
      }
    } catch (e) {
      emit(EvenementsError(
        message: 'Erreur lors de la création de l\'événement: $e',
        error: e,
      ));
    }
  }

  /// Met à jour un événement
  Future<void> _onUpdateEvenement(
    UpdateEvenement event,
    Emitter<EvenementsState> emit,
  ) async {
    try {
      emit(const EvenementsLoading());

      final evenement = await _updateEvent(event.id, event.evenement);

      emit(EvenementUpdated(evenement));
    } on DioException catch (e) {
      if (e.response?.statusCode == 400) {
        final errors = _extractValidationErrors(e.response?.data);
        emit(EvenementsValidationError(
          message: 'Erreur de validation',
          validationErrors: errors,
          code: '400',
        ));
      } else {
        emit(EvenementsNetworkError(
          message: _getNetworkErrorMessage(e),
          code: e.response?.statusCode.toString(),
          error: e,
        ));
      }
    } catch (e) {
      emit(EvenementsError(
        message: 'Erreur lors de la mise à jour de l\'événement: $e',
        error: e,
      ));
    }
  }

  /// Supprime un événement
  Future<void> _onDeleteEvenement(
    DeleteEvenement event,
    Emitter<EvenementsState> emit,
  ) async {
    try {
      emit(const EvenementsLoading());

      await _deleteEvent(event.id);

      emit(EvenementDeleted(event.id));
    } on DioException catch (e) {
      emit(EvenementsNetworkError(
        message: _getNetworkErrorMessage(e),
        code: e.response?.statusCode.toString(),
        error: e,
      ));
    } catch (e) {
      emit(EvenementsError(
        message: 'Erreur lors de la suppression de l\'événement: $e',
        error: e,
      ));
    }
  }

  /// Charge les événements à venir
  Future<void> _onLoadEvenementsAVenir(
    LoadEvenementsAVenir event,
    Emitter<EvenementsState> emit,
  ) async {
    try {
      emit(const EvenementsLoading());

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

      emit(EvenementsLoaded(
        evenements: result.evenements,
        total: result.total,
        page: result.page,
        size: result.size,
        totalPages: result.totalPages,
      ));
    } on DioException catch (e) {
      emit(EvenementsNetworkError(
        message: _getNetworkErrorMessage(e),
        code: e.response?.statusCode.toString(),
        error: e,
      ));
    } catch (e) {
      emit(EvenementsError(
        message: 'Erreur lors du chargement des événements à venir: $e',
        error: e,
      ));
    }
  }

  /// Charge les événements en cours
  Future<void> _onLoadEvenementsEnCours(
    LoadEvenementsEnCours event,
    Emitter<EvenementsState> emit,
  ) async {
    try {
      emit(const EvenementsLoading());

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

      emit(EvenementsLoaded(
        evenements: result.evenements,
        total: result.total,
        page: result.page,
        size: result.size,
        totalPages: result.totalPages,
      ));
    } on DioException catch (e) {
      emit(EvenementsNetworkError(
        message: _getNetworkErrorMessage(e),
        code: e.response?.statusCode.toString(),
        error: e,
      ));
    } catch (e) {
      emit(EvenementsError(
        message: 'Erreur lors du chargement des événements en cours: $e',
        error: e,
      ));
    }
  }

  /// Charge les événements passés
  Future<void> _onLoadEvenementsPasses(
    LoadEvenementsPasses event,
    Emitter<EvenementsState> emit,
  ) async {
    try {
      emit(const EvenementsLoading());

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

      emit(EvenementsLoaded(
        evenements: result.evenements,
        total: result.total,
        page: result.page,
        size: result.size,
        totalPages: result.totalPages,
      ));
    } on DioException catch (e) {
      emit(EvenementsNetworkError(
        message: _getNetworkErrorMessage(e),
        code: e.response?.statusCode.toString(),
        error: e,
      ));
    } catch (e) {
      emit(EvenementsError(
        message: 'Erreur lors du chargement des événements passés: $e',
        error: e,
      ));
    }
  }

  /// S'inscrire à un événement
  Future<void> _onInscrireEvenement(
    InscrireEvenement event,
    Emitter<EvenementsState> emit,
  ) async {
    try {
      emit(const EvenementsLoading());

      await _registerForEvent(event.evenementId);

      emit(EvenementInscrit(event.evenementId));
    } on DioException catch (e) {
      emit(EvenementsNetworkError(
        message: _getNetworkErrorMessage(e),
        code: e.response?.statusCode.toString(),
        error: e,
      ));
    } catch (e) {
      emit(EvenementsError(
        message: 'Erreur lors de l\'inscription à l\'événement: $e',
        error: e,
      ));
    }
  }

  /// Se désinscrire d'un événement
  Future<void> _onDesinscrireEvenement(
    DesinscrireEvenement event,
    Emitter<EvenementsState> emit,
  ) async {
    try {
      emit(const EvenementsLoading());

      await _cancelRegistration(event.evenementId);

      emit(EvenementDesinscrit(event.evenementId));
    } on DioException catch (e) {
      emit(EvenementsNetworkError(
        message: _getNetworkErrorMessage(e),
        code: e.response?.statusCode.toString(),
        error: e,
      ));
    } catch (e) {
      emit(EvenementsError(
        message: 'Erreur lors de la désinscription de l\'événement: $e',
        error: e,
      ));
    }
  }

  /// Charge les participants
  Future<void> _onLoadParticipants(
    LoadParticipants event,
    Emitter<EvenementsState> emit,
  ) async {
    try {
      emit(const EvenementsLoading());

      final participants = await _getEventParticipants(event.evenementId);

      emit(ParticipantsLoaded(
        evenementId: event.evenementId,
        participants: participants,
      ));
    } on DioException catch (e) {
      emit(EvenementsNetworkError(
        message: _getNetworkErrorMessage(e),
        code: e.response?.statusCode.toString(),
        error: e,
      ));
    } catch (e) {
      emit(EvenementsError(
        message: 'Erreur lors du chargement des participants: $e',
        error: e,
      ));
    }
  }

  /// Charge les statistiques
  Future<void> _onLoadEvenementsStats(
    LoadEvenementsStats event,
    Emitter<EvenementsState> emit,
  ) async {
    try {
      emit(const EvenementsLoading());

      final stats = await _repository.getEvenementsStats();

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

  /// Extrait les erreurs de validation
  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.';
    }
  }
}

