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/product/product.dart';

part 'product_loader_event.dart';
part 'product_loader_state.dart';
part 'product_loader_bloc.freezed.dart';

@injectable
class ProductLoaderBloc extends Bloc<ProductLoaderEvent, ProductLoaderState> {
  final IProductRepository _productRepository;

  ProductLoaderBloc(this._productRepository)
    : super(ProductLoaderState.initial()) {
    on<ProductLoaderEvent>(_onProductLoaderEvent);
  }

  Future<void> _onProductLoaderEvent(
    ProductLoaderEvent event,
    Emitter<ProductLoaderState> emit,
  ) {
    return event.map(
      categoryIdChanged: (e) async {
        emit(state.copyWith(categoryId: e.categoryId));
      },
      searchChanged: (e) async {
        emit(state.copyWith(search: e.search));
      },
      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<ProductLoaderState> _mapFetchedToState(
    ProductLoaderState state, {
    bool isRefresh = false,
  }) async {
    state = state.copyWith(isFetching: false);

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

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

    final failureOrProduct = await _productRepository.get(
      categoryId: state.categoryId,
      page: state.page,
      search: state.search,
    );

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

    return state;
  }
}
