import 'package:dartz/dartz.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:injectable/injectable.dart';

import '../../../domain/outlet/outlet.dart';

part 'outlet_list_loader_event.dart';
part 'outlet_list_loader_state.dart';
part 'outlet_list_loader_bloc.freezed.dart';

@injectable
class OutletListLoaderBloc
    extends Bloc<OutletListLoaderEvent, OutletListLoaderState> {
  final IOutletRepository _outletRepository;

  OutletListLoaderBloc(this._outletRepository)
    : super(OutletListLoaderState.initial()) {
    on<OutletListLoaderEvent>(_onOutletListLoaderEvent);
  }

  Future<void> _onOutletListLoaderEvent(
    OutletListLoaderEvent event,
    Emitter<OutletListLoaderState> emit,
  ) {
    return event.map(
      searchChanged: (e) async {
        emit(state.copyWith(search: e.search));
      },
      isActiveChanged: (e) async {
        emit(state.copyWith(isActive: e.isActive));
      },
      fetched: (e) async {
        var newState = state;

        if (e.isRefresh) {
          newState = state.copyWith(isFetching: true);
          emit(newState);
        }

        newState = await _mapFetchedToState(state, isRefresh: e.isRefresh);

        emit(newState);
      },
    );
  }

  Future<OutletListLoaderState> _mapFetchedToState(
    OutletListLoaderState state, {
    bool isRefresh = false,
  }) async {
    state = state.copyWith(isFetching: false);

    if (state.hasReachedMax && state.outlets.isNotEmpty && !isRefresh) {
      return state;
    }

    if (isRefresh) {
      state = state.copyWith(
        page: 1,
        failureOptionOutlet: none(),
        hasReachedMax: false,
        outlets: [],
      );
    }

    final failureOrOutlets = await _outletRepository.getList(
      page: state.page,
      search: state.search,
      isActive: state.isActive,
    );

    state = failureOrOutlets.fold(
      (f) {
        if (state.outlets.isNotEmpty) {
          return state.copyWith(hasReachedMax: true);
        }
        return state.copyWith(failureOptionOutlet: optionOf(f));
      },
      (outlets) {
        return state.copyWith(
          outlets: List.from(state.outlets)..addAll(outlets),
          failureOptionOutlet: none(),
          page: state.page + 1,
          hasReachedMax: outlets.length < 10,
        );
      },
    );

    return state;
  }
}
