import 'dart:async';
import 'dart:convert';

import 'package:boilerplate/data/models/product/get_product_list_req_model.dart';
import 'package:boilerplate/domain/entities/product/product_data.dart';
import 'package:boilerplate/domain/entities/product/product_list_filter_data.dart';
import 'package:boilerplate/domain/usecases/categories/get_categories_use_case.dart';
import 'package:boilerplate/domain/usecases/categories/get_intellectuall_type_use_case.dart';
import 'package:boilerplate/domain/usecases/product/get_product_list_use_case.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:injectable/injectable.dart';

part 'products_event.dart';
part 'products_state.dart';

@injectable
class ProductListBloc extends Bloc<ProductListEvent, ProductListState> {
  final GetProductListUseCase _getProductListUseCase;
  final GetIntellectualTypesUseCase _getIntellectualTypesUseCase;
  final GetCategoriesUseCase _getCategoriesUseCase;

  final int _limit = 999;

  ProductListFilterData? _tempFilter;
  int _page = 1;
  String _searchTxt = '';
  String _sort = '';
  String _sortKey = '';
  ProductListBloc(
    this._getProductListUseCase,
    this._getIntellectualTypesUseCase,
    this._getCategoriesUseCase,
  ) : super(InitialState(ProductListStateData())) {
    on<GetProductsEvent>(_onGetProducts);
    on<SelectProductEvent>(_onSelectProduct);
    on<GetProductFilterEvent>(_onGetPRoductFilter);
    //Filter
    on<OpenFilterEvent>(_onOpenFilter);
    on<SelectIntellectualFilterEvent>(_onSelectIntellectualFilter);
    on<SelectCategoriesFilterEvent>(_onSelectCategoriesFilter);
    on<InputPriceFromEvent>(_onInputPriceFrom);
    on<InputPriceEndEvent>(_onInputPriceEnd);
    on<SelectSortEndEvent>(_onInputSort);
    on<SelectPriceChoiceFilterEvent>(_onSelectPriceChoiceFilter);
    on<CleanFilterEvent>(_onCleanFilter);
    on<CloseFilterEvent>(_onCloseFilter);
    on<SubmitFilterEvent>(_onSubmitFilter);
    on<LoadMoreEvent>(_onLoadMore);
    on<RefreshEvent>(_onRefreshEvent);
    on<ShowRefreshEvent>(_onShowRefresh);
  }

  Future<FutureOr<void>> _onGetProducts(
    GetProductsEvent event,
    Emitter<ProductListState> emit,
  ) async {
    final inte = state.data.filterData?.intellectualSelected ?? [];

    final cate = state.data.filterData?.categoriesSelected ?? [];

    final priceFrom = _getPriceFrom();
    final priceTo = _getPriceTo();
    final mySort = state.data.filterData?.sort ?? "";
    final mySortKey = state.data.filterData?.sortKey ?? "";
    print("key > ${mySort} ${mySortKey}");
    _searchTxt = event.searchTxt.isNotEmpty ? event.searchTxt : _searchTxt;

    final result = await _getProductListUseCase.execute(GetProductListReqModel(
      search: _searchTxt,
      page: _page,
      limit: _limit,
      intellectualType: jsonEncode(inte),
      category: jsonEncode(cate),
      priceFrom: priceFrom,
      priceTo: priceTo,
      sort: mySort,
      sort_key: mySortKey,
    ));

    result.fold((products) {
      emit(GetProductsSuccessState(state.data.copyWith(
        newProducts: [
          ...state.data.products,
          ...products,
        ],
        newCanLoadMore: products.isNotEmpty && products.length == _limit,
      )));
    }, (fail) {
      emit(GetProductsFailState(state.data));
    });
  }

  String? _getPriceFrom() {
    if (state.data.filterData != null) {
      if (state.data.filterData!.priceChoice != FilterPriceChoice.empty) {
        return state.data.filterData!.priceChoice.from.toString();
      } else {
        return (state.data.filterData?.startPrice ?? 0) > 0
            ? state.data.filterData!.startPrice.toString()
            : null;
      }
    }
    return null;
  }

  String? _getPriceTo() {
    if (state.data.filterData != null) {
      if (state.data.filterData!.priceChoice != FilterPriceChoice.empty) {
        return state.data.filterData!.priceChoice.end.toString();
      } else {
        return (state.data.filterData?.endPrice ?? 0) > 0
            ? state.data.filterData.toString()
            : null;
      }
    }
    return null;
  }

  FutureOr<void> _onSelectProduct(
    SelectProductEvent event,
    Emitter<ProductListState> emit,
  ) {
    emit(ProductSelectedState(state.data.copyWith(
      newSelected: event.product,
    )));
  }

  Future<FutureOr<void>> _onGetPRoductFilter(
    GetProductFilterEvent event,
    Emitter<ProductListState> emit,
  ) async {
    ProductListFilterData filter =
        state.data.filterData ?? ProductListFilterData();

    final intellectualResult = await _getIntellectualTypesUseCase.execute();

    intellectualResult.fold((itellectual) {
      filter = filter.copyWith(
        newIntellectualType: itellectual,
      );
    }, (r) => null);

    final categoriesResult = await _getCategoriesUseCase.execute();

    categoriesResult.fold((categories) {
      filter = filter.copyWith(
        newCategories: categories,
      );
    }, (fail) => null);

    emit(GetProductFilterSuccessState(state.data.copyWith(
      newFilterData: filter,
    )));
  }

  FutureOr<void> _onOpenFilter(
    OpenFilterEvent event,
    Emitter<ProductListState> emit,
  ) {
    _tempFilter = state.data.filterData ?? ProductListFilterData();
  }

  FutureOr<void> _onSelectIntellectualFilter(
    SelectIntellectualFilterEvent event,
    Emitter<ProductListState> emit,
  ) {
    List<int> listIntellectual =
        state.data.filterData?.intellectualSelected ?? [];

    if (listIntellectual.where((element) => element == event.id).isNotEmpty) {
      listIntellectual.remove(event.id);
    } else {
      listIntellectual = [...listIntellectual, event.id];
    }

    final filter = (state.data.filterData ?? ProductListFilterData()).copyWith(
      newIntellectualSelected: listIntellectual,
    );

    emit(UpdateFilterState(state.data.copyWith(
      newFilterData: filter,
    )));
  }

  FutureOr<void> _onSelectCategoriesFilter(
    SelectCategoriesFilterEvent event,
    Emitter<ProductListState> emit,
  ) {
    List<int> listCategories = state.data.filterData?.categoriesSelected ?? [];

    if (listCategories.where((element) => element == event.id).isNotEmpty) {
      listCategories.remove(event.id);
    } else {
      listCategories = [...listCategories, event.id];
    }

    final filter = (state.data.filterData ?? ProductListFilterData()).copyWith(
      newCategoriesSelected: listCategories,
    );

    emit(UpdateFilterState(state.data.copyWith(
      newFilterData: filter,
    )));
  }

  FutureOr<void> _onInputPriceFrom(
    InputPriceFromEvent event,
    Emitter<ProductListState> emit,
  ) {
    final filter = state.data.filterData!.copyWith(
      newPriceChoice: FilterPriceChoice.empty,
      newStartPrice: double.parse(event.text),
    );

    emit(UpdateFilterState(state.data.copyWith(
      newFilterData: filter,
    )));
  }

  FutureOr<void> _onInputPriceEnd(
    InputPriceEndEvent event,
    Emitter<ProductListState> emit,
  ) {
    final filter = state.data.filterData!.copyWith(
      newPriceChoice: FilterPriceChoice.empty,
      newEndPrice: double.parse(event.text),
    );

    emit(UpdateFilterState(state.data.copyWith(
      newFilterData: filter,
    )));
  }

  FutureOr<void> _onInputSort(
    SelectSortEndEvent event,
    Emitter<ProductListState> emit,
  ) {
    final filter = state.data.filterData!.copyWith(
      newSort: event.sort,
      newSortKey: event.sort_key,

    );

    emit(UpdateFilterState(state.data.copyWith(
      newFilterData: filter,
    )));
  }

  FutureOr<void> _onSelectPriceChoiceFilter(
    SelectPriceChoiceFilterEvent event,
    Emitter<ProductListState> emit,
  ) {
    final filter = state.data.filterData?.copyWith(
      newPriceChoice: event.choice != state.data.filterData?.priceChoice
          ? event.choice
          : FilterPriceChoice.empty,
    );

    emit(UpdateFilterState(state.data.copyWith(
      newFilterData: filter,
    )));
  }

  FutureOr<void> _onCleanFilter(
    CleanFilterEvent event,
    Emitter<ProductListState> emit,
  ) {
    emit(CleanFilterState(state.data.copyWith(
      newFilterData: ProductListFilterData(
        intellectualType: state.data.filterData!.intellectualType,
        categories: state.data.filterData!.categories,
      ),
    )));
  }

  FutureOr<void> _onCloseFilter(
    CloseFilterEvent event,
    Emitter<ProductListState> emit,
  ) {
    emit(CloseFilterState(state.data.copyWith(
      newFilterData: _tempFilter,
    )));
  }

  FutureOr<void> _onSubmitFilter(
    SubmitFilterEvent event,
    Emitter<ProductListState> emit,
  ) {
    emit(SumitFilterState(state.data));
  }

  FutureOr<void> _onLoadMore(
    LoadMoreEvent event,
    Emitter<ProductListState> emit,
  ) {
    _page += 1;

    add(GetProductsEvent(searchTxt: _searchTxt));
  }

  FutureOr<void> _onRefreshEvent(
    RefreshEvent event,
    Emitter<ProductListState> emit,
  ) {
    emit(RefreshState(state.data.copyWith(
      newProducts: [],
    )));

    add(GetProductsEvent());
  }

  FutureOr<void> _onShowRefresh(
    ShowRefreshEvent event,
    Emitter<ProductListState> emit,
  ) {
    _searchTxt = event.textSearch ?? _searchTxt;
    emit(ShowRefreshState(state.data));
  }
}
