import 'dart:ui';

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

import '../../../domain/table/table.dart';

part 'table_loader_event.dart';
part 'table_loader_state.dart';
part 'table_loader_bloc.freezed.dart';

@injectable
class TableLoaderBloc extends Bloc<TableLoaderEvent, TableLoaderState> {
  final ITableRepository _repository;
  TableLoaderBloc(this._repository) : super(TableLoaderState.initial()) {
    on<TableLoaderEvent>(_onTableLoadedEvent);
  }

  Future<void> _onTableLoadedEvent(
    TableLoaderEvent event,
    Emitter<TableLoaderState> emit,
  ) {
    return event.map(
      setSelectedTable: (e) async {
        if (e.table == null) {
          emit(state.copyWith(selectedTable: null));
        } else {
          if (e.table?.status.isAvailable ?? false) {
            emit(state.copyWith(selectedTable: e.table));
          }
        }
      },
      fetched: (e) async {
        var newState = state;

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

        newState = await _mapFetchedToState(
          newState,
          isRefresh: e.isRefresh,
          status: e.status,
        );
        emit(newState);
      },
      updatedPostion: (e) async {
        final updatedTables = state.tables.map((table) {
          if (table.id == e.id) {
            return table.copyWith(
              positionX: e.position.dx,
              positionY: e.position.dy,
            );
          }
          return table;
        }).toList();

        emit(state.copyWith(tables: updatedTables));
      },
    );
  }

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

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

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

    final failureOrTable = await _repository.fetchTables(
      page: state.page,
      status: status,
    );

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

    return state;
  }
}
