import 'dart:developer' as developer;

import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_domain/krow_domain.dart';
import '../../domain/arguments/assign_nfc_tag_arguments.dart';
import '../../domain/arguments/create_hub_arguments.dart';
import '../../domain/arguments/delete_hub_arguments.dart';
import '../../domain/usecases/assign_nfc_tag_usecase.dart';
import '../../domain/usecases/create_hub_usecase.dart';
import '../../domain/usecases/delete_hub_usecase.dart';
import '../../domain/usecases/get_hubs_usecase.dart';
import 'client_hubs_event.dart';
import 'client_hubs_state.dart';

/// BLoC responsible for managing the state of the Client Hubs feature.
///
/// It orchestrates the flow between the UI and the domain layer by invoking
/// specific use cases for fetching, creating, deleting, and assigning tags to hubs.
class ClientHubsBloc extends Bloc<ClientHubsEvent, ClientHubsState>
    implements Disposable {
  final GetHubsUseCase _getHubsUseCase;
  final CreateHubUseCase _createHubUseCase;
  final DeleteHubUseCase _deleteHubUseCase;
  final AssignNfcTagUseCase _assignNfcTagUseCase;

  ClientHubsBloc({
    required GetHubsUseCase getHubsUseCase,
    required CreateHubUseCase createHubUseCase,
    required DeleteHubUseCase deleteHubUseCase,
    required AssignNfcTagUseCase assignNfcTagUseCase,
  }) : _getHubsUseCase = getHubsUseCase,
       _createHubUseCase = createHubUseCase,
       _deleteHubUseCase = deleteHubUseCase,
       _assignNfcTagUseCase = assignNfcTagUseCase,
       super(const ClientHubsState()) {
    on<ClientHubsFetched>(_onFetched);
    on<ClientHubsAddRequested>(_onAddRequested);
    on<ClientHubsDeleteRequested>(_onDeleteRequested);
    on<ClientHubsNfcTagAssignRequested>(_onNfcTagAssignRequested);
    on<ClientHubsMessageCleared>(_onMessageCleared);
    on<ClientHubsAddDialogToggled>(_onAddDialogToggled);
    on<ClientHubsIdentifyDialogToggled>(_onIdentifyDialogToggled);
  }

  void _onAddDialogToggled(
    ClientHubsAddDialogToggled event,
    Emitter<ClientHubsState> emit,
  ) {
    emit(state.copyWith(showAddHubDialog: event.visible));
  }

  void _onIdentifyDialogToggled(
    ClientHubsIdentifyDialogToggled event,
    Emitter<ClientHubsState> emit,
  ) {
    if (event.hub == null) {
      emit(state.copyWith(clearHubToIdentify: true));
    } else {
      emit(state.copyWith(hubToIdentify: event.hub));
    }
  }

  Future<void> _onFetched(
    ClientHubsFetched event,
    Emitter<ClientHubsState> emit,
  ) async {
    emit(state.copyWith(status: ClientHubsStatus.loading));
    try {
      final List<Hub> hubs = await _getHubsUseCase();
      emit(state.copyWith(status: ClientHubsStatus.success, hubs: hubs));
    } on AppException catch (e) {
      developer.log('Error ${e.code}: ${e.technicalMessage}', name: 'ClientHubsBloc');
      emit(
        state.copyWith(
          status: ClientHubsStatus.failure,
          errorMessage: e.messageKey,
        ),
      );
    } catch (e) {
      developer.log('Unexpected error: $e', name: 'ClientHubsBloc');
      emit(
        state.copyWith(
          status: ClientHubsStatus.failure,
          errorMessage: 'errors.generic.unknown',
        ),
      );
    }
  }

  Future<void> _onAddRequested(
    ClientHubsAddRequested event,
    Emitter<ClientHubsState> emit,
  ) async {
    emit(state.copyWith(status: ClientHubsStatus.actionInProgress));
    try {
      await _createHubUseCase(
        CreateHubArguments(
          name: event.name,
          address: event.address,
          placeId: event.placeId,
          latitude: event.latitude,
          longitude: event.longitude,
          city: event.city,
          state: event.state,
          street: event.street,
          country: event.country,
          zipCode: event.zipCode,
        ),
      );
      final List<Hub> hubs = await _getHubsUseCase();
      emit(
        state.copyWith(
          status: ClientHubsStatus.actionSuccess,
          hubs: hubs,
          successMessage: 'Hub created successfully',
          showAddHubDialog: false,
        ),
      );
    } on AppException catch (e) {
      developer.log('Error ${e.code}: ${e.technicalMessage}', name: 'ClientHubsBloc');
      emit(
        state.copyWith(
          status: ClientHubsStatus.actionFailure,
          errorMessage: e.messageKey,
        ),
      );
    } catch (e) {
      developer.log('Unexpected error: $e', name: 'ClientHubsBloc');
      emit(
        state.copyWith(
          status: ClientHubsStatus.actionFailure,
          errorMessage: 'errors.generic.unknown',
        ),
      );
    }
  }

  Future<void> _onDeleteRequested(
    ClientHubsDeleteRequested event,
    Emitter<ClientHubsState> emit,
  ) async {
    emit(state.copyWith(status: ClientHubsStatus.actionInProgress));
    try {
      await _deleteHubUseCase(DeleteHubArguments(hubId: event.hubId));
      final List<Hub> hubs = await _getHubsUseCase();
      emit(
        state.copyWith(
          status: ClientHubsStatus.actionSuccess,
          hubs: hubs,
          successMessage: 'Hub deleted successfully',
        ),
      );
    } on AppException catch (e) {
      developer.log('Error ${e.code}: ${e.technicalMessage}', name: 'ClientHubsBloc');
      emit(
        state.copyWith(
          status: ClientHubsStatus.actionFailure,
          errorMessage: e.messageKey,
        ),
      );
    } catch (e) {
      developer.log('Unexpected error: $e', name: 'ClientHubsBloc');
      emit(
        state.copyWith(
          status: ClientHubsStatus.actionFailure,
          errorMessage: 'errors.generic.unknown',
        ),
      );
    }
  }

  Future<void> _onNfcTagAssignRequested(
    ClientHubsNfcTagAssignRequested event,
    Emitter<ClientHubsState> emit,
  ) async {
    emit(state.copyWith(status: ClientHubsStatus.actionInProgress));
    try {
      await _assignNfcTagUseCase(
        AssignNfcTagArguments(hubId: event.hubId, nfcTagId: event.nfcTagId),
      );
      final List<Hub> hubs = await _getHubsUseCase();
      emit(
        state.copyWith(
          status: ClientHubsStatus.actionSuccess,
          hubs: hubs,
          successMessage: 'NFC tag assigned successfully',
          clearHubToIdentify: true,
        ),
      );
    } on AppException catch (e) {
      developer.log('Error ${e.code}: ${e.technicalMessage}', name: 'ClientHubsBloc');
      emit(
        state.copyWith(
          status: ClientHubsStatus.actionFailure,
          errorMessage: e.messageKey,
        ),
      );
    } catch (e) {
      developer.log('Unexpected error: $e', name: 'ClientHubsBloc');
      emit(
        state.copyWith(
          status: ClientHubsStatus.actionFailure,
          errorMessage: 'errors.generic.unknown',
        ),
      );
    }
  }

  void _onMessageCleared(
    ClientHubsMessageCleared event,
    Emitter<ClientHubsState> emit,
  ) {
    emit(
      state.copyWith(
        clearErrorMessage: true,
        clearSuccessMessage: true,
        status:
            state.status == ClientHubsStatus.actionSuccess ||
                state.status == ClientHubsStatus.actionFailure
            ? ClientHubsStatus.success
            : state.status,
      ),
    );
  }

  @override
  void dispose() {
    close();
  }
}
