/// BLoC pour la gestion des organisations (Clean Architecture)
library organizations_bloc;

import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:injectable/injectable.dart';
import '../data/models/organization_model.dart';
import '../data/services/organization_service.dart';
import '../domain/usecases/get_organizations.dart';
import '../domain/usecases/get_organization_by_id.dart';
import '../domain/usecases/create_organization.dart' as uc;
import '../domain/usecases/update_organization.dart' as uc;
import '../domain/usecases/delete_organization.dart' as uc;
import '../domain/usecases/get_organization_members.dart';
import '../domain/usecases/update_organization_config.dart';
import '../domain/repositories/organization_repository.dart';
import 'organizations_event.dart';
import 'organizations_state.dart';

/// BLoC principal pour la gestion des organisations
@injectable
class OrganizationsBloc extends Bloc<OrganizationsEvent, OrganizationsState> {
  final GetOrganizations _getOrganizations;
  final GetOrganizationById _getOrganizationById;
  final uc.CreateOrganization _createOrganization;
  final uc.UpdateOrganization _updateOrganization;
  final uc.DeleteOrganization _deleteOrganization;
  final GetOrganizationMembers _getOrganizationMembers;
  final UpdateOrganizationConfig _updateOrganizationConfig;
  final IOrganizationRepository _repository; // Pour méthodes non-couvertes (activate, suspend, search, stats)
  final OrganizationService _organizationService; // Pour helpers (sort, filter local)

  OrganizationsBloc(
    this._getOrganizations,
    this._getOrganizationById,
    this._createOrganization,
    this._updateOrganization,
    this._deleteOrganization,
    this._getOrganizationMembers,
    this._updateOrganizationConfig,
    this._repository,
    this._organizationService,
  ) : super(const OrganizationsInitial()) {
    // Enregistrement des handlers d'événements
    on<LoadOrganizations>(_onLoadOrganizations);
    on<LoadMoreOrganizations>(_onLoadMoreOrganizations);
    on<SearchOrganizations>(_onSearchOrganizations);
    on<AdvancedSearchOrganizations>(_onAdvancedSearchOrganizations);
    on<LoadOrganizationById>(_onLoadOrganizationById);
    on<CreateOrganization>(_onCreateOrganization);
    on<UpdateOrganization>(_onUpdateOrganization);
    on<DeleteOrganization>(_onDeleteOrganization);
    on<ActivateOrganization>(_onActivateOrganization);
    on<SuspendOrganization>(_onSuspendOrganization);
    on<FilterOrganizationsByStatus>(_onFilterOrganizationsByStatus);
    on<FilterOrganizationsByType>(_onFilterOrganizationsByType);
    on<SortOrganizations>(_onSortOrganizations);
    on<LoadOrganizationsStats>(_onLoadOrganizationsStats);
    on<ClearOrganizationsFilters>(_onClearOrganizationsFilters);
    on<RefreshOrganizations>(_onRefreshOrganizations);
    on<ResetOrganizationsState>(_onResetOrganizationsState);
  }

  /// Charge la liste des organisations
  Future<void> _onLoadOrganizations(
    LoadOrganizations event,
    Emitter<OrganizationsState> emit,
  ) async {
    try {
      if (event.refresh || state is! OrganizationsLoaded) {
        emit(const OrganizationsLoading());
      }

      List<OrganizationModel> listToUse;
      List<String>? filterIds;
      if (event.useMesOnly) {
        // Admin d'organisation : endpoint backend /mes retourne uniquement ses organisations
        listToUse = await _repository.getMesOrganisations();
        filterIds = listToUse.map((o) => o.id).whereType<String>().toList();
      } else {
        final organizations = await _getOrganizations(
          page: event.page,
          size: event.size,
          recherche: event.recherche,
        );
        if (event.filterOrganizationIds != null &&
            event.filterOrganizationIds!.isNotEmpty) {
          final allowedIds = event.filterOrganizationIds!.toSet();
          listToUse = organizations
              .where((org) => org.id != null && allowedIds.contains(org.id))
              .toList();
          filterIds = event.filterOrganizationIds;
        } else {
          listToUse = organizations;
          filterIds = null;
        }
      }

      emit(OrganizationsLoaded(
        organizations: listToUse,
        filteredOrganizations: listToUse,
        hasReachedMax: event.useMesOnly || listToUse.length < event.size,
        currentPage: event.page,
        currentSearch: event.recherche,
        filterOrganizationIds: filterIds,
        useMesOnly: event.useMesOnly,
      ));
    } catch (e) {
      emit(OrganizationsError(
        'Erreur lors du chargement des organisations',
        details: e.toString(),
      ));
    }
  }

  /// Charge plus d'organisations (pagination)
  Future<void> _onLoadMoreOrganizations(
    LoadMoreOrganizations event,
    Emitter<OrganizationsState> emit,
  ) async {
    final currentState = state;
    if (currentState is! OrganizationsLoaded || currentState.hasReachedMax) {
      return;
    }

    emit(OrganizationsLoadingMore(currentState.organizations));

    try {
      final nextPage = currentState.currentPage + 1;
      final newOrganizations = await _getOrganizations(
        page: nextPage,
        size: 20,
        recherche: currentState.currentSearch,
      );

      var allOrganizations = [...currentState.organizations, ...newOrganizations];
      // Réappliquer le filtre orgAdmin si présent
      if (currentState.filterOrganizationIds != null &&
          currentState.filterOrganizationIds!.isNotEmpty) {
        final allowedIds = currentState.filterOrganizationIds!.toSet();
        allOrganizations = allOrganizations
            .where((org) => org.id != null && allowedIds.contains(org.id))
            .toList();
      }
      final filteredOrganizations = _applyCurrentFilters(allOrganizations, currentState);

      emit(currentState.copyWith(
        organizations: allOrganizations,
        filteredOrganizations: filteredOrganizations,
        hasReachedMax: newOrganizations.length < 20,
        currentPage: nextPage,
      ));
    } catch (e) {
      emit(OrganizationsError(
        'Erreur lors du chargement de plus d\'organisations',
        details: e.toString(),
        previousOrganizations: currentState.organizations,
      ));
    }
  }

  /// Recherche des organisations
  Future<void> _onSearchOrganizations(
    SearchOrganizations event,
    Emitter<OrganizationsState> emit,
  ) async {
    final currentState = state;
    if (currentState is! OrganizationsLoaded) {
      // Si pas encore chargé, charger avec recherche
      add(LoadOrganizations(recherche: event.query, refresh: true));
      return;
    }

    try {
      if (event.query.isEmpty) {
        // Recherche vide, afficher toutes les organisations
        final filteredOrganizations = _applyCurrentFilters(
          currentState.organizations,
          currentState.copyWith(clearSearch: true),
        );
        emit(currentState.copyWith(
          filteredOrganizations: filteredOrganizations,
          clearSearch: true,
        ));
      } else {
        // Recherche locale d'abord
        final localResults = _organizationService.searchLocal(
          currentState.organizations,
          event.query,
        );

        emit(currentState.copyWith(
          filteredOrganizations: localResults,
          currentSearch: event.query,
        ));

        // Puis recherche serveur pour plus de résultats
        final serverResults = await _getOrganizations(
          page: 0,
          size: 50,
          recherche: event.query,
        );

        final filteredResults = _applyCurrentFilters(serverResults, currentState);
        emit(currentState.copyWith(
          organizations: serverResults,
          filteredOrganizations: filteredResults,
          currentSearch: event.query,
          currentPage: 0,
          hasReachedMax: true,
        ));
      }
    } catch (e) {
      emit(OrganizationsError(
        'Erreur lors de la recherche',
        details: e.toString(),
        previousOrganizations: currentState.organizations,
      ));
    }
  }

  /// Recherche avancée
  Future<void> _onAdvancedSearchOrganizations(
    AdvancedSearchOrganizations event,
    Emitter<OrganizationsState> emit,
  ) async {
    emit(const OrganizationsLoading());

    try {
      final organizations = await _repository.searchOrganizations(
        nom: event.nom,
        type: event.type,
        statut: event.statut,
        ville: event.ville,
        region: event.region,
        pays: event.pays,
        page: event.page,
        size: event.size,
      );

      emit(OrganizationsLoaded(
        organizations: organizations,
        filteredOrganizations: organizations,
        hasReachedMax: organizations.length < event.size,
        currentPage: event.page,
        typeFilter: event.type,
        statusFilter: event.statut,
      ));
    } catch (e) {
      emit(OrganizationsError(
        'Erreur lors de la recherche avancée',
        details: e.toString(),
      ));
    }
  }

  /// Charge une organisation par ID
  Future<void> _onLoadOrganizationById(
    LoadOrganizationById event,
    Emitter<OrganizationsState> emit,
  ) async {
    emit(OrganizationLoading(event.id));

    try {
      final organization = await _getOrganizationById(event.id);
      if (organization != null) {
        emit(OrganizationLoaded(organization));
      } else {
        emit(OrganizationError('Organisation non trouvée', organizationId: event.id));
      }
    } catch (e) {
      emit(OrganizationError(
        'Erreur lors du chargement de l\'organisation',
        organizationId: event.id,
      ));
    }
  }

  /// Crée une nouvelle organisation
  Future<void> _onCreateOrganization(
    CreateOrganization event,
    Emitter<OrganizationsState> emit,
  ) async {
    emit(const OrganizationCreating());

    try {
      final createdOrganization = await _createOrganization(event.organization);
      emit(OrganizationCreated(createdOrganization));

      // Recharger la liste si elle était déjà chargée
      if (state is OrganizationsLoaded) {
        add(const RefreshOrganizations());
      }
    } catch (e) {
      emit(OrganizationsError(
        'Erreur lors de la création de l\'organisation',
        details: e.toString(),
      ));
    }
  }

  /// Met à jour une organisation
  Future<void> _onUpdateOrganization(
    UpdateOrganization event,
    Emitter<OrganizationsState> emit,
  ) async {
    emit(OrganizationUpdating(event.id));

    try {
      final updatedOrganization = await _updateOrganization(
        event.id,
        event.organization,
      );
      emit(OrganizationUpdated(updatedOrganization));

      // Mettre à jour la liste si elle était déjà chargée
      final currentState = state;
      if (currentState is OrganizationsLoaded) {
        final updatedList = currentState.organizations.map((org) {
          return org.id == event.id ? updatedOrganization : org;
        }).toList();

        final filteredList = _applyCurrentFilters(updatedList, currentState);
        emit(currentState.copyWith(
          organizations: updatedList,
          filteredOrganizations: filteredList,
        ));
      }
    } catch (e) {
      emit(OrganizationsError(
        'Erreur lors de la mise à jour de l\'organisation',
        details: e.toString(),
      ));
    }
  }

  /// Supprime une organisation
  Future<void> _onDeleteOrganization(
    DeleteOrganization event,
    Emitter<OrganizationsState> emit,
  ) async {
    emit(OrganizationDeleting(event.id));

    try {
      await _deleteOrganization(event.id);
      emit(OrganizationDeleted(event.id));

      // Retirer de la liste si elle était déjà chargée
      final currentState = state;
      if (currentState is OrganizationsLoaded) {
        final updatedList = currentState.organizations.where((org) => org.id != event.id).toList();
        final filteredList = _applyCurrentFilters(updatedList, currentState);
        emit(currentState.copyWith(
          organizations: updatedList,
          filteredOrganizations: filteredList,
        ));
      }
    } catch (e) {
      emit(OrganizationsError(
        'Erreur lors de la suppression de l\'organisation',
        details: e.toString(),
      ));
    }
  }

  /// Active une organisation
  Future<void> _onActivateOrganization(
    ActivateOrganization event,
    Emitter<OrganizationsState> emit,
  ) async {
    emit(OrganizationActivating(event.id));

    try {
      final activatedOrganization = await _repository.activateOrganization(event.id);
      emit(OrganizationActivated(activatedOrganization));

      // Mettre à jour la liste si elle était déjà chargée
      final currentState = state;
      if (currentState is OrganizationsLoaded) {
        final updatedList = currentState.organizations.map((org) {
          return org.id == event.id ? activatedOrganization : org;
        }).toList();

        final filteredList = _applyCurrentFilters(updatedList, currentState);
        emit(currentState.copyWith(
          organizations: updatedList,
          filteredOrganizations: filteredList,
        ));
      }
    } catch (e) {
      emit(OrganizationsError(
        'Erreur lors de l\'activation de l\'organisation',
        details: e.toString(),
      ));
    }
  }

  /// Suspend une organisation
  Future<void> _onSuspendOrganization(
    SuspendOrganization event,
    Emitter<OrganizationsState> emit,
  ) async {
    emit(OrganizationSuspending(event.id));

    try {
      final suspendedOrganization = await _repository.suspendOrganization(event.id);
      emit(OrganizationSuspended(suspendedOrganization));

      // Mettre à jour la liste si elle était déjà chargée
      final currentState = state;
      if (currentState is OrganizationsLoaded) {
        final updatedList = currentState.organizations.map((org) {
          return org.id == event.id ? suspendedOrganization : org;
        }).toList();

        final filteredList = _applyCurrentFilters(updatedList, currentState);
        emit(currentState.copyWith(
          organizations: updatedList,
          filteredOrganizations: filteredList,
        ));
      }
    } catch (e) {
      emit(OrganizationsError(
        'Erreur lors de la suspension de l\'organisation',
        details: e.toString(),
      ));
    }
  }

  /// Filtre par statut
  void _onFilterOrganizationsByStatus(
    FilterOrganizationsByStatus event,
    Emitter<OrganizationsState> emit,
  ) {
    final currentState = state;
    if (currentState is! OrganizationsLoaded) return;

    final filteredOrganizations = _applyCurrentFilters(
      currentState.organizations,
      currentState.copyWith(statusFilter: event.statut),
    );

    emit(currentState.copyWith(
      filteredOrganizations: filteredOrganizations,
      statusFilter: event.statut,
    ));
  }

  /// Filtre par type
  void _onFilterOrganizationsByType(
    FilterOrganizationsByType event,
    Emitter<OrganizationsState> emit,
  ) {
    final currentState = state;
    if (currentState is! OrganizationsLoaded) return;

    final filteredOrganizations = _applyCurrentFilters(
      currentState.organizations,
      currentState.copyWith(typeFilter: event.type),
    );

    emit(currentState.copyWith(
      filteredOrganizations: filteredOrganizations,
      typeFilter: event.type,
    ));
  }

  /// Trie les organisations
  void _onSortOrganizations(
    SortOrganizations event,
    Emitter<OrganizationsState> emit,
  ) {
    final currentState = state;
    if (currentState is! OrganizationsLoaded) return;

    List<OrganizationModel> sortedOrganizations;
    switch (event.sortType) {
      case OrganizationSortType.name:
        sortedOrganizations = _organizationService.sortByName(
          currentState.filteredOrganizations,
          ascending: event.ascending,
        );
        break;
      case OrganizationSortType.creationDate:
        sortedOrganizations = _organizationService.sortByCreationDate(
          currentState.filteredOrganizations,
          ascending: event.ascending,
        );
        break;
      case OrganizationSortType.memberCount:
        sortedOrganizations = _organizationService.sortByMemberCount(
          currentState.filteredOrganizations,
          ascending: event.ascending,
        );
        break;
      default:
        sortedOrganizations = currentState.filteredOrganizations;
    }

    emit(currentState.copyWith(
      filteredOrganizations: sortedOrganizations,
      sortType: event.sortType,
      sortAscending: event.ascending,
    ));
  }

  /// Charge les statistiques
  Future<void> _onLoadOrganizationsStats(
    LoadOrganizationsStats event,
    Emitter<OrganizationsState> emit,
  ) async {
    emit(const OrganizationsStatsLoading());

    try {
      final stats = await _repository.getOrganizationsStats();
      emit(OrganizationsStatsLoaded(stats));
    } catch (e) {
      emit(const OrganizationsStatsError('Erreur lors du chargement des statistiques'));
    }
  }

  /// Efface les filtres
  void _onClearOrganizationsFilters(
    ClearOrganizationsFilters event,
    Emitter<OrganizationsState> emit,
  ) {
    final currentState = state;
    if (currentState is! OrganizationsLoaded) return;

    emit(currentState.copyWith(
      filteredOrganizations: currentState.organizations,
      clearSearch: true,
      clearStatusFilter: true,
      clearTypeFilter: true,
      clearSort: true,
    ));
  }

  /// Rafraîchit les données
  void _onRefreshOrganizations(
    RefreshOrganizations event,
    Emitter<OrganizationsState> emit,
  ) {
    final currentState = state;
    if (currentState is OrganizationsLoaded && currentState.useMesOnly) {
      add(const LoadOrganizations(refresh: true, useMesOnly: true));
    } else {
      final filterIds = currentState is OrganizationsLoaded
          ? currentState.filterOrganizationIds
          : null;
      add(LoadOrganizations(refresh: true, filterOrganizationIds: filterIds));
    }
  }

  /// Remet à zéro l'état
  void _onResetOrganizationsState(
    ResetOrganizationsState event,
    Emitter<OrganizationsState> emit,
  ) {
    emit(const OrganizationsInitial());
  }

  /// Applique les filtres actuels à une liste d'organisations
  List<OrganizationModel> _applyCurrentFilters(
    List<OrganizationModel> organizations,
    OrganizationsLoaded state,
  ) {
    var filtered = organizations;

    // Filtre par recherche
    if (state.currentSearch?.isNotEmpty == true) {
      filtered = _organizationService.searchLocal(filtered, state.currentSearch!);
    }

    // Filtre par statut
    if (state.statusFilter != null) {
      filtered = _organizationService.filterByStatus(filtered, state.statusFilter!);
    }

    // Filtre par type
    if (state.typeFilter != null) {
      filtered = _organizationService.filterByType(filtered, state.typeFilter!);
    }

    return filtered;
  }
}
