import 'dart:async';
import 'package:injectable/injectable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.dart';
import '../../domain/entities/dashboard_entity.dart';
import '../../domain/usecases/get_dashboard_data.dart';
import '../../../../core/error/failures.dart';
import '../../../../core/websocket/websocket_service.dart';
import '../../../../core/utils/logger.dart';

part 'dashboard_event.dart';
part 'dashboard_state.dart';

@injectable
class DashboardBloc extends Bloc<DashboardEvent, DashboardState> {
  final GetDashboardData getDashboardData;
  final GetDashboardStats getDashboardStats;
  final GetRecentActivities getRecentActivities;
  final GetUpcomingEvents getUpcomingEvents;
  final WebSocketService webSocketService;

  StreamSubscription<WebSocketEvent>? _webSocketEventSubscription;
  StreamSubscription<bool>? _webSocketConnectionSubscription;

  DashboardBloc({
    required this.getDashboardData,
    required this.getDashboardStats,
    required this.getRecentActivities,
    required this.getUpcomingEvents,
    required this.webSocketService,
  }) : super(DashboardInitial()) {
    on<LoadDashboardData>(_onLoadDashboardData);
    on<RefreshDashboardData>(_onRefreshDashboardData);
    on<LoadDashboardStats>(_onLoadDashboardStats);
    on<LoadRecentActivities>(_onLoadRecentActivities);
    on<LoadUpcomingEvents>(_onLoadUpcomingEvents);
    on<RefreshDashboardFromWebSocket>(_onRefreshDashboardFromWebSocket);
    on<WebSocketConnectionChanged>(_onWebSocketConnectionChanged);

    // Initialiser WebSocket et écouter les events
    _initializeWebSocket();
  }

  /// Initialise la connexion WebSocket et écoute les events
  void _initializeWebSocket() {
    // Connexion au WebSocket
    webSocketService.connect();
    AppLogger.info('DashboardBloc: WebSocket initialisé');

    // Écouter les events WebSocket
    _webSocketEventSubscription = webSocketService.eventStream.listen(
      (event) {
        AppLogger.info('DashboardBloc: Event WebSocket reçu - ${event.eventType}');

        // Dispatcher uniquement les events pertinents au dashboard
        if (event is DashboardStatsEvent) {
          add(RefreshDashboardFromWebSocket(event.data));
        } else if (event is FinanceApprovalEvent) {
          // Les approbations affectent les stats, rafraîchir
          add(RefreshDashboardFromWebSocket(event.data));
        } else if (event is MemberEvent) {
          // Les changements de membres affectent les stats
          add(RefreshDashboardFromWebSocket(event.data));
        } else if (event is ContributionEvent) {
          // Les cotisations affectent les stats financières
          add(RefreshDashboardFromWebSocket(event.data));
        }
      },
      onError: (error) {
        AppLogger.error('DashboardBloc: Erreur WebSocket', error: error);
      },
    );

    // Écouter le statut de connexion WebSocket
    _webSocketConnectionSubscription = webSocketService.connectionStatusStream.listen(
      (isConnected) {
        AppLogger.info('DashboardBloc: WebSocket ${isConnected ? "connecté" : "déconnecté"}');
        add(WebSocketConnectionChanged(isConnected));
      },
    );
  }

  Future<void> _onLoadDashboardData(
    LoadDashboardData event,
    Emitter<DashboardState> emit,
  ) async {
    emit(DashboardLoading());

    final result = await getDashboardData(
      GetDashboardDataParams(
        organizationId: event.organizationId,
        userId: event.userId,
      ),
    );

    result.fold(
      (failure) => emit(DashboardError(_mapFailureToMessage(failure))),
      (dashboardData) => emit(DashboardLoaded(dashboardData)),
    );
  }

  Future<void> _onRefreshDashboardData(
    RefreshDashboardData event,
    Emitter<DashboardState> emit,
  ) async {
    // Garde l'état actuel pendant le refresh
    if (state is DashboardLoaded) {
      emit(DashboardRefreshing((state as DashboardLoaded).dashboardData));
    } else {
      emit(DashboardLoading());
    }

    final result = await getDashboardData(
      GetDashboardDataParams(
        organizationId: event.organizationId,
        userId: event.userId,
      ),
    );

    result.fold(
      (failure) => emit(DashboardError(_mapFailureToMessage(failure))),
      (dashboardData) => emit(DashboardLoaded(dashboardData)),
    );
  }

  Future<void> _onLoadDashboardStats(
    LoadDashboardStats event,
    Emitter<DashboardState> emit,
  ) async {
    final result = await getDashboardStats(
      GetDashboardStatsParams(
        organizationId: event.organizationId,
        userId: event.userId,
      ),
    );

    result.fold(
      (failure) => emit(DashboardError(_mapFailureToMessage(failure))),
      (stats) {
        if (state is DashboardLoaded) {
          final currentData = (state as DashboardLoaded).dashboardData;
          final updatedData = DashboardEntity(
            stats: stats,
            recentActivities: currentData.recentActivities,
            upcomingEvents: currentData.upcomingEvents,
            userPreferences: currentData.userPreferences,
            organizationId: currentData.organizationId,
            userId: currentData.userId,
          );
          emit(DashboardLoaded(updatedData));
        }
      },
    );
  }

  Future<void> _onLoadRecentActivities(
    LoadRecentActivities event,
    Emitter<DashboardState> emit,
  ) async {
    final result = await getRecentActivities(
      GetRecentActivitiesParams(
        organizationId: event.organizationId,
        userId: event.userId,
        limit: event.limit,
      ),
    );

    result.fold(
      (failure) => emit(DashboardError(_mapFailureToMessage(failure))),
      (activities) {
        if (state is DashboardLoaded) {
          final currentData = (state as DashboardLoaded).dashboardData;
          final updatedData = DashboardEntity(
            stats: currentData.stats,
            recentActivities: activities,
            upcomingEvents: currentData.upcomingEvents,
            userPreferences: currentData.userPreferences,
            organizationId: currentData.organizationId,
            userId: currentData.userId,
          );
          emit(DashboardLoaded(updatedData));
        }
      },
    );
  }

  Future<void> _onLoadUpcomingEvents(
    LoadUpcomingEvents event,
    Emitter<DashboardState> emit,
  ) async {
    final result = await getUpcomingEvents(
      GetUpcomingEventsParams(
        organizationId: event.organizationId,
        userId: event.userId,
        limit: event.limit,
      ),
    );

    result.fold(
      (failure) => emit(DashboardError(_mapFailureToMessage(failure))),
      (events) {
        if (state is DashboardLoaded) {
          final currentData = (state as DashboardLoaded).dashboardData;
          final updatedData = DashboardEntity(
            stats: currentData.stats,
            recentActivities: currentData.recentActivities,
            upcomingEvents: events,
            userPreferences: currentData.userPreferences,
            organizationId: currentData.organizationId,
            userId: currentData.userId,
          );
          emit(DashboardLoaded(updatedData));
        }
      },
    );
  }

  /// Rafraîchit le dashboard suite à un event WebSocket
  Future<void> _onRefreshDashboardFromWebSocket(
    RefreshDashboardFromWebSocket event,
    Emitter<DashboardState> emit,
  ) async {
    AppLogger.info('DashboardBloc: Rafraîchissement depuis WebSocket');

    // Si le dashboard est chargé, on rafraîchit uniquement les stats
    // pour éviter de recharger toutes les données
    if (state is DashboardLoaded) {
      final currentData = (state as DashboardLoaded).dashboardData;

      // Rafraîchir les stats depuis le backend
      final result = await getDashboardStats(
        GetDashboardStatsParams(
          organizationId: currentData.organizationId,
          userId: currentData.userId,
        ),
      );

      result.fold(
        (failure) {
          AppLogger.error('Erreur rafraîchissement stats WebSocket', error: failure);
          // Ne pas émettre d'erreur, garder les données actuelles
        },
        (stats) {
          final updatedData = DashboardEntity(
            stats: stats,
            recentActivities: currentData.recentActivities,
            upcomingEvents: currentData.upcomingEvents,
            userPreferences: currentData.userPreferences,
            organizationId: currentData.organizationId,
            userId: currentData.userId,
          );
          emit(DashboardLoaded(updatedData));
          AppLogger.info('DashboardBloc: Stats rafraîchies depuis WebSocket');
        },
      );
    }
  }

  /// Gère les changements de statut de connexion WebSocket
  void _onWebSocketConnectionChanged(
    WebSocketConnectionChanged event,
    Emitter<DashboardState> emit,
  ) {
    // Pour l'instant, on log juste le statut
    // On pourrait ajouter un indicateur visuel dans l'UI plus tard
    if (event.isConnected) {
      AppLogger.info('DashboardBloc: WebSocket connecté - Temps réel actif');
    } else {
      AppLogger.warning('DashboardBloc: WebSocket déconnecté - Reconnexion en cours...');
    }
  }

  String _mapFailureToMessage(Failure failure) {
    switch (failure.runtimeType) {
      case ServerFailure:
        return 'Erreur serveur. Veuillez réessayer.';
      case NetworkFailure:
        return 'Pas de connexion internet. Vérifiez votre connexion.';
      default:
        return 'Une erreur inattendue s\'est produite.';
    }
  }

  @override
  Future<void> close() {
    // Annuler les subscriptions WebSocket
    _webSocketEventSubscription?.cancel();
    _webSocketConnectionSubscription?.cancel();

    // Déconnecter le WebSocket
    webSocketService.disconnect();

    AppLogger.info('DashboardBloc: Fermé et WebSocket déconnecté');

    return super.close();
  }
}
