// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

import 'package:architecture_example/core/enums/fetch_status.dart';
import 'package:architecture_example/domain/entities/album.dart';
import 'package:architecture_example/domain/usecases/photos/params/query_parameters.dart';
import 'package:architecture_example/domain/usecases/photos/retrieve_all_albums.dart';
import 'package:bloc_concurrency/bloc_concurrency.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:stream_transform/stream_transform.dart';

part 'album_event.dart';
part 'album_state.dart';

const _albumLimit = 20;
const throttleDuration = Duration(milliseconds: 100);

EventTransformer<E> throttleDroppable<E>(Duration duration) =>
    (events, mapper) => droppable<E>().call(events.throttle(duration), mapper);

class AlbumBloc extends Bloc<AlbumEvent, AlbumState> {

  AlbumBloc(this._retrieveAllAlbums) : super(const AlbumState()) {
    on<AlbumFetched>(
      _onAlbumFetched,
      transformer: throttleDroppable(throttleDuration),
    );
  }
  final RetrieveAllAlbums _retrieveAllAlbums;

  Future<void> _onAlbumFetched(
    AlbumFetched event,
    Emitter<AlbumState> emit,
  ) async {
    if (state.hasReachedMax) {
      return;
    }
    if (state.status == FetchStatus.initial) {
      final albums =
          await _retrieveAllAlbums.call(QueryParameters(0, _albumLimit));
      return emit(
        albums.fold(
          (value) => state.copyWith(
            status: FetchStatus.success,
            albums: value,
            hasReachedMax: false,
          ),
          (error) => state.copyWith(
            status: FetchStatus.failure,
            albums: [],
            hasReachedMax: false,
          ),
        ),
      );
    }
    final albums = await _retrieveAllAlbums
        .call(QueryParameters(state.albums.length, _albumLimit));
    return emit(
      albums.fold(
        (value) {
          if (value.isEmpty) {
            return state.copyWith(
              hasReachedMax: true,
            );
          }
          return state.copyWith(
            status: FetchStatus.success,
            albums: List.of(state.albums)..addAll(value),
            hasReachedMax: false,
          );
        },
        (error) => state.copyWith(
          status: FetchStatus.failure,
          albums: List.of(state.albums),
          hasReachedMax: false,
        ),
      ),
    );
  }
}
