import 'package:firebase_data_connect/firebase_data_connect.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
import 'package:krow_domain/krow_domain.dart';
import '../../domain/arguments/one_time_order_arguments.dart';
import '../../domain/usecases/create_one_time_order_usecase.dart';
import 'one_time_order_event.dart';
import 'one_time_order_state.dart';

/// BLoC for managing the multi-step one-time order creation form.
class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState> {
  OneTimeOrderBloc(this._createOneTimeOrderUseCase, this._dataConnect)
    : super(OneTimeOrderState.initial()) {
    on<OneTimeOrderVendorsLoaded>(_onVendorsLoaded);
    on<OneTimeOrderVendorChanged>(_onVendorChanged);
    on<OneTimeOrderHubsLoaded>(_onHubsLoaded);
    on<OneTimeOrderHubChanged>(_onHubChanged);
    on<OneTimeOrderEventNameChanged>(_onEventNameChanged);
    on<OneTimeOrderDateChanged>(_onDateChanged);
    on<OneTimeOrderPositionAdded>(_onPositionAdded);
    on<OneTimeOrderPositionRemoved>(_onPositionRemoved);
    on<OneTimeOrderPositionUpdated>(_onPositionUpdated);
    on<OneTimeOrderSubmitted>(_onSubmitted);

    _loadVendors();
    _loadHubs();
  }
  final CreateOneTimeOrderUseCase _createOneTimeOrderUseCase;
  final dc.ExampleConnector _dataConnect;

  Future<void> _loadVendors() async {
    try {
      final QueryResult<dc.ListVendorsData, void> result = await _dataConnect.listVendors().execute();
      final List<Vendor> vendors = result.data.vendors
          .map(
            (dc.ListVendorsVendors vendor) => Vendor(
              id: vendor.id,
              name: vendor.companyName,
              rates: const <String, double>{},
            ),
          )
          .toList();
      add(OneTimeOrderVendorsLoaded(vendors));
    } catch (_) {
      add(const OneTimeOrderVendorsLoaded(<Vendor>[]));
    }
  }

  Future<void> _loadRolesForVendor(String vendorId) async {
    try {
      final QueryResult<dc.ListRolesByVendorIdData, dc.ListRolesByVendorIdVariables> result = await _dataConnect.listRolesByVendorId(
        vendorId: vendorId,
      ).execute();
      final List<OneTimeOrderRoleOption> roles = result.data.roles
          .map(
            (dc.ListRolesByVendorIdRoles role) => OneTimeOrderRoleOption(
              id: role.id,
              name: role.name,
              costPerHour: role.costPerHour,
            ),
          )
          .toList();
      emit(state.copyWith(roles: roles));
    } catch (_) {
      emit(state.copyWith(roles: const <OneTimeOrderRoleOption>[]));
    }
  }

  Future<void> _loadHubs() async {
    try {
      final String? businessId = dc.ClientSessionStore.instance.session?.business?.id;
      if (businessId == null || businessId.isEmpty) {
        add(const OneTimeOrderHubsLoaded(<OneTimeOrderHubOption>[]));
        return;
      }
      final QueryResult<dc.ListTeamHubsByOwnerIdData, dc.ListTeamHubsByOwnerIdVariables>
      result = await _dataConnect.listTeamHubsByOwnerId(ownerId: businessId).execute();
      final List<OneTimeOrderHubOption> hubs = result.data.teamHubs
          .map(
            (dc.ListTeamHubsByOwnerIdTeamHubs hub) => OneTimeOrderHubOption(
              id: hub.id,
              name: hub.hubName,
              address: hub.address,
              placeId: hub.placeId,
              latitude: hub.latitude,
              longitude: hub.longitude,
              city: hub.city,
              state: hub.state,
              street: hub.street,
              country: hub.country,
              zipCode: hub.zipCode,
            ),
          )
          .toList();
      add(OneTimeOrderHubsLoaded(hubs));
    } catch (_) {
      add(const OneTimeOrderHubsLoaded(<OneTimeOrderHubOption>[]));
    }
  }

  void _onVendorsLoaded(
    OneTimeOrderVendorsLoaded event,
    Emitter<OneTimeOrderState> emit,
  ) {
    final Vendor? selectedVendor =
        event.vendors.isNotEmpty ? event.vendors.first : null;
    emit(
      state.copyWith(
        vendors: event.vendors,
        selectedVendor: selectedVendor,
      ),
    );
    if (selectedVendor != null) {
      _loadRolesForVendor(selectedVendor.id);
    }
  }

  void _onVendorChanged(
    OneTimeOrderVendorChanged event,
    Emitter<OneTimeOrderState> emit,
  ) {
    emit(state.copyWith(selectedVendor: event.vendor));
    _loadRolesForVendor(event.vendor.id);
  }

  void _onHubsLoaded(
    OneTimeOrderHubsLoaded event,
    Emitter<OneTimeOrderState> emit,
  ) {
    final OneTimeOrderHubOption? selectedHub =
        event.hubs.isNotEmpty ? event.hubs.first : null;
    emit(
      state.copyWith(
        hubs: event.hubs,
        selectedHub: selectedHub,
        location: selectedHub?.name ?? '',
      ),
    );
  }

  void _onHubChanged(
    OneTimeOrderHubChanged event,
    Emitter<OneTimeOrderState> emit,
  ) {
    emit(
      state.copyWith(
        selectedHub: event.hub,
        location: event.hub.name,
      ),
    );
  }

  void _onEventNameChanged(
    OneTimeOrderEventNameChanged event,
    Emitter<OneTimeOrderState> emit,
  ) {
    emit(state.copyWith(eventName: event.eventName));
  }

  void _onDateChanged(
    OneTimeOrderDateChanged event,
    Emitter<OneTimeOrderState> emit,
  ) {
    emit(state.copyWith(date: event.date));
  }

  void _onPositionAdded(
    OneTimeOrderPositionAdded event,
    Emitter<OneTimeOrderState> emit,
  ) {
    final List<OneTimeOrderPosition> newPositions =
        List<OneTimeOrderPosition>.from(state.positions)..add(
          const OneTimeOrderPosition(
            role: '',
            count: 1,
            startTime: '09:00',
            endTime: '17:00',
          ),
        );
    emit(state.copyWith(positions: newPositions));
  }

  void _onPositionRemoved(
    OneTimeOrderPositionRemoved event,
    Emitter<OneTimeOrderState> emit,
  ) {
    if (state.positions.length > 1) {
      final List<OneTimeOrderPosition> newPositions =
          List<OneTimeOrderPosition>.from(state.positions)
            ..removeAt(event.index);
      emit(state.copyWith(positions: newPositions));
    }
  }

  void _onPositionUpdated(
    OneTimeOrderPositionUpdated event,
    Emitter<OneTimeOrderState> emit,
  ) {
    final List<OneTimeOrderPosition> newPositions =
        List<OneTimeOrderPosition>.from(state.positions);
    newPositions[event.index] = event.position;
    emit(state.copyWith(positions: newPositions));
  }

  Future<void> _onSubmitted(
    OneTimeOrderSubmitted event,
    Emitter<OneTimeOrderState> emit,
  ) async {
    emit(state.copyWith(status: OneTimeOrderStatus.loading));
    try {
      final Map<String, double> roleRates = <String, double>{
        for (final OneTimeOrderRoleOption role in state.roles) role.id: role.costPerHour,
      };
      final OneTimeOrderHubOption? selectedHub = state.selectedHub;
      if (selectedHub == null) {
        throw Exception('Hub is missing.');
      }
      final OneTimeOrder order = OneTimeOrder(
        date: state.date,
        location: selectedHub.name,
        positions: state.positions,
        hub: OneTimeOrderHubDetails(
          id: selectedHub.id,
          name: selectedHub.name,
          address: selectedHub.address,
          placeId: selectedHub.placeId,
          latitude: selectedHub.latitude,
          longitude: selectedHub.longitude,
          city: selectedHub.city,
          state: selectedHub.state,
          street: selectedHub.street,
          country: selectedHub.country,
          zipCode: selectedHub.zipCode,
        ),
        eventName: state.eventName,
        vendorId: state.selectedVendor?.id,
        roleRates: roleRates,
      );
      await _createOneTimeOrderUseCase(OneTimeOrderArguments(order: order));
      emit(state.copyWith(status: OneTimeOrderStatus.success));
    } catch (e) {
      emit(
        state.copyWith(
          status: OneTimeOrderStatus.failure,
          errorMessage: e.toString(),
        ),
      );
    }
  }
}
