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<OneTimeOrderDateChanged>(_onDateChanged);
    on<OneTimeOrderLocationChanged>(_onLocationChanged);
    on<OneTimeOrderPositionAdded>(_onPositionAdded);
    on<OneTimeOrderPositionRemoved>(_onPositionRemoved);
    on<OneTimeOrderPositionUpdated>(_onPositionUpdated);
    on<OneTimeOrderSubmitted>(_onSubmitted);

    _loadVendors();
  }
  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>[]));
    }
  }

  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 _onDateChanged(
    OneTimeOrderDateChanged event,
    Emitter<OneTimeOrderState> emit,
  ) {
    emit(state.copyWith(date: event.date));
  }

  void _onLocationChanged(
    OneTimeOrderLocationChanged event,
    Emitter<OneTimeOrderState> emit,
  ) {
    emit(state.copyWith(location: event.location));
  }

  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 OneTimeOrder order = OneTimeOrder(
        date: state.date,
        location: state.location,
        positions: state.positions,
        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(),
        ),
      );
    }
  }
}
