import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow/core/application/di/injectable.dart';
import 'package:krow/core/entity/event_entity.dart';
import 'package:krow/features/events/domain/blocs/events_list_bloc/events_event.dart';
import 'package:krow/features/events/domain/blocs/events_list_bloc/events_state.dart';
import 'package:krow/features/events/domain/events_repository.dart';

class EventsBloc extends Bloc<EventsEvent, EventsState> {
  EventsBloc()
      : super(const EventsState(tabs: {
          0: {
            0: EventTabState(
                items: [],
                inLoading: false,
                hasMoreItems: true,
                status: EventStatus.pending),
            1: EventTabState(
                items: [],
                inLoading: false,
                hasMoreItems: true,
                status: EventStatus.assigned),
            2: EventTabState(
                items: [],
                inLoading: false,
                hasMoreItems: true,
                status: EventStatus.confirmed),
          },
          1: {
            0: EventTabState(
                items: [],
                inLoading: false,
                hasMoreItems: true,
                status: EventStatus.active),
            1: EventTabState(
                items: [],
                inLoading: false,
                hasMoreItems: true,
                status: EventStatus.finished),
          },
          2: {
            0: EventTabState(
                items: [],
                inLoading: false,
                hasMoreItems: true,
                status: EventStatus.completed),
            1: EventTabState(
                items: [],
                inLoading: false,
                hasMoreItems: true,
                status: EventStatus.closed),
            2: EventTabState(
                items: [],
                inLoading: false,
                hasMoreItems: true,
                status: EventStatus.canceled),
          },
          3: {
            0: EventTabState(
                items: [],
                inLoading: false,
                hasMoreItems: true,
                status: EventStatus.draft),
          },
        })) {
    on<EventsInitialEvent>(_onInitial);
    on<EventsTabChangedEvent>(_onTabChanged);
    on<EventsSubTabChangedEvent>(_onSubTabChanged);
    on<LoadTabEventEvent>(_onLoadTabItems);
    on<LoadMoreEventEvent>(_onLoadMoreTabItems);

    getIt<EventsRepository>().statusStream.listen((event) {
      (int, int)? pair = findTabIndexForStatus(state.tabs, event);
      if (pair != null) {
        add(LoadTabEventEvent(tabIndex: pair.$1, subTabIndex: pair.$2));
      }
    });
  }

  Future<void> _onInitial(EventsInitialEvent event, emit) async {
    add(const LoadTabEventEvent(tabIndex: 0, subTabIndex: 0));
  }

  Future<void> _onTabChanged(EventsTabChangedEvent event, emit) async {
    emit(state.copyWith(tabIndex: event.tabIndex, subTabIndex: 0));
    final currentTabState = state.tabs[event.tabIndex]![0]!;
    if (currentTabState.items.isEmpty && !currentTabState.inLoading) {
      add(LoadTabEventEvent(tabIndex: event.tabIndex, subTabIndex: 0));
    }
  }

  Future<void> _onSubTabChanged(EventsSubTabChangedEvent event, emit) async {
    emit(state.copyWith(subTabIndex: event.subTabIndex));
    final currentTabState = state.tabs[state.tabIndex]![event.subTabIndex]!;
    if (currentTabState.items.isEmpty && !currentTabState.inLoading) {
      add(LoadTabEventEvent(
          tabIndex: state.tabIndex, subTabIndex: event.subTabIndex));
    }
  }

  Future<void> _onLoadTabItems(LoadTabEventEvent event, emit) async {
    await _fetchEvents(event.tabIndex, event.subTabIndex, null, emit);
  }

  Future<void> _onLoadMoreTabItems(LoadMoreEventEvent event, emit) async {
    final currentTabState = state.tabs[event.tabIndex]![event.subTabIndex]!;
    if (!currentTabState.hasMoreItems || currentTabState.inLoading) return;
    await _fetchEvents(
        event.tabIndex, event.subTabIndex, currentTabState.items, emit);
  }

  _fetchEvents(int tabIndex, int subTabIndex, List<EventEntity>? previousItems,
      emit) async {
    if (previousItems != null && previousItems.lastOrNull?.cursor == null) {
      return;
    }
    final currentTabState = state.tabs[tabIndex]![subTabIndex]!;
    var newState = state.copyWith(
      tabs: {
        ...state.tabs,
        tabIndex: {
          ...state.tabs[tabIndex]!,
          subTabIndex: currentTabState.copyWith(inLoading: true)
        },
      },
    );
    emit(newState);
    await Future.delayed(const Duration(seconds: 1));
    try {
      var items = await getIt<EventsRepository>().getEvents(
        statusFilter: currentTabState.status,
        lastItemId: previousItems?.lastOrNull?.cursor,
      );

      var newState = state.copyWith(
        tabs: {
          ...state.tabs,
          tabIndex: {
            ...state.tabs[tabIndex]!,
            subTabIndex: currentTabState.copyWith(
              items: (previousItems ?? [])..addAll(items),
              hasMoreItems: items.isNotEmpty,
              inLoading: false,
            )
          },
        },
      );
      emit(newState);
    } catch (e) {
      debugPrint(e.toString());
      emit(state.copyWith(errorMessage: e.toString()));
      emit(state.copyWith(
        tabs: {
          ...state.tabs,
          tabIndex: {
            ...state.tabs[tabIndex]!,
            subTabIndex: currentTabState.copyWith(
              items: (previousItems ?? []),
              hasMoreItems: false,
              inLoading: false,
            )
          },
        },
      ));
    }
  }

  @override
  Future<void> close() {
    getIt<EventsRepository>().dispose();
    return super.close();
  }

  (int, int)? findTabIndexForStatus(
      Map<int, Map<int, EventTabState>> tabs, EventStatus status) {
    for (var tabEntry in tabs.entries) {
      for (var subTabEntry in tabEntry.value.entries) {
        if (subTabEntry.value.status == status) {
          return (tabEntry.key, subTabEntry.key);
        }
      }
    }
    return null;
  }
}
