import 'dart:developer';

import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow/core/application/di/injectable.dart';
import 'package:krow/core/data/enums/pagination_status.dart';
import 'package:krow/core/data/enums/state_status.dart';
import 'package:krow/features/earning/domain/entities/earning_shift_entity.dart';
import 'package:krow/features/earning/domain/entities/earnings_summary_entity.dart';
import 'package:krow/features/earning/domain/staff_earning_repository.dart';

part 'earnings_data.dart';
part 'earnings_event.dart';
part 'earnings_state.dart';

class EarningsBloc extends Bloc<EarningsEvent, EarningsState> {
  EarningsBloc()
      : super(const EarningsState.initial()
          ..copyWith(
            tabs: defaultEarningTabs,
          )) {
    on<EarningsInitEvent>(_onInit);
    on<EarningsSwitchBalancePeriodEvent>(_onSwitchBalancePeriod);
    on<EarningsTabChangedEvent>(_onTabChanged);
    on<LoadAdditionalEarnings>(_onLoadAdditionalEarnings);
    on<ReloadCurrentEarnings>(_onReloadCurrentEarnings);
    on<ConfirmStaffEarning>(_onConfirmStaffEarning);
    on<DisputeStaffEarning>(_onDisputeStaffEarning);
  }

  final _earningRepository = getIt<StaffEarningRepository>();

  Future<void> _onInit(
    EarningsInitEvent event,
    Emitter<EarningsState> emit,
  ) async {
    add(const LoadAdditionalEarnings());

    emit(state.copyWith(status: StateStatus.loading));
    EarningsSummaryEntity? earningsData;
    try {
      earningsData = await _earningRepository.getStaffEarningsData();
    } catch (except) {
      log('Error in EarningsBloc, on EarningsInitEvent', error: except);
    }
    emit(state.copyWith(earnings: earningsData, status: StateStatus.idle));
  }

  void _onSwitchBalancePeriod(
    EarningsSwitchBalancePeriodEvent event,
    Emitter<EarningsState> emit,
  ) {
    emit(state.copyWith(balancePeriod: event.period));
  }

  void _onTabChanged(
    EarningsTabChangedEvent event,
    Emitter<EarningsState> emit,
  ) {
    emit(state.copyWith(tabIndex: event.tabIndex));

    if (state.currentTab.status == PaginationStatus.initial) {
      add(const LoadAdditionalEarnings());
    }
  }

  Future<void> _onLoadAdditionalEarnings(
    LoadAdditionalEarnings event,
    Emitter<EarningsState> emit,
  ) async {
    if (!state.currentTab.status.allowLoad) return;
    emit(state.updateCurrentTab(status: PaginationStatus.loading));

    final tabIndex = state.tabIndex;
    try {
      final earningsBatch = await _earningRepository.getEarningsBatch(
        status: state.currentTab.loadingKey,
        lastEntryCursor: state.currentTab.paginationCursor,
      );

      emit(
        state.copyWithTab(
          tabIndex: tabIndex,
          tab: state.tabs[tabIndex].copyWith(
            status: earningsBatch.hasNextBatch
                ? PaginationStatus.idle
                : PaginationStatus.end,
            items: [...state.tabs[tabIndex].items, ...earningsBatch.earnings],
            hasMoreItems: earningsBatch.hasNextBatch,
          ),
        ),
      );
    } catch (except) {
      log(
        'Error in EarningsBloc, on LoadAdditionalEarnings',
        error: except,
      );
      emit(
        state.copyWithTab(
          tabIndex: tabIndex,
          tab: state.tabs[tabIndex].copyWith(
            status: PaginationStatus.error,
          ),
        ),
      );
    }

    if (state.tabs[tabIndex].status == PaginationStatus.loading) {
      emit(
        state.copyWithTab(
          tabIndex: tabIndex,
          tab: state.tabs[tabIndex].copyWith(
            status: PaginationStatus.idle,
          ),
        ),
      );
    }
  }

  void _onReloadCurrentEarnings(
    ReloadCurrentEarnings event,
    Emitter<EarningsState> emit,
  ) {
    emit(
      state.copyWithTab(
        tab: EarningsTabState.initial(
          label: state.currentTab.label,
          loadingKey: state.currentTab.loadingKey,
        ),
      ),
    );

    add(const LoadAdditionalEarnings());
  }

  Future<void> _onEarningAction(
    Future<EarningShiftEntity?> earningFuture,
    String eventName,
    Emitter<EarningsState> emit,
  ) async {
    emit(state.copyWith(status: StateStatus.loading));

    EarningShiftEntity? earning;
    try {
      earning = await earningFuture;
    } catch (except) {
      log(
        'Error in EarningsBloc, on $eventName',
        error: except,
      );
    }

    var tabData = state.currentTab;
    if (earning != null) {
      final earningIndex =
          state.currentTab.items.indexWhere((item) => item.id == earning?.id);

      if (earningIndex >= 0) {
        tabData = state.currentTab.copyWith(
          items: List.from(state.currentTab.items)..[earningIndex] = earning,
        );
      }
    }

    emit(
      state.copyWithTab(
        status: StateStatus.idle,
        tab: tabData,
      ),
    );
  }

  Future<void> _onConfirmStaffEarning(
    ConfirmStaffEarning event,
    Emitter<EarningsState> emit,
  ) {
    return _onEarningAction(
      _earningRepository.confirmStaffEarning(
        earningId: event.earningId,
      ),
      'ConfirmStaffEarning',
      emit,
    );
  }

  Future<void> _onDisputeStaffEarning(
    DisputeStaffEarning event,
    Emitter<EarningsState> emit,
  ) async {
    return _onEarningAction(
      _earningRepository.disputeStaffEarning(
        id: event.earningId,
        reason: event.reason,
        details: event.details,
      ),
      'DeclineStaffEarning',
      emit,
    );
  }
}
