// 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/photo.dart';
import 'package:architecture_example/domain/usecases/photos/open_album.dart';
import 'package:architecture_example/domain/usecases/photos/params/query_parameters.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 'photo_event.dart';
part 'photo_state.dart';

const _photoLimit = 40;
const throttleDuration = Duration(milliseconds: 100);

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

class PhotoBloc extends Bloc<PhotoEvent, PhotoState> {

  PhotoBloc(this._openAlbum) : super(const PhotoState()) {
    on<PhotoFetched>(
      _onPhotoFetched,
      transformer: throttleDroppable(throttleDuration),
    );
  }
  final OpenAlbum _openAlbum;

  Future<void> _onPhotoFetched(
    PhotoFetched event,
    Emitter<PhotoState> emit,
  ) async {
    if (state.hasReachedMax) {
      return;
    }
    if (state.status == FetchStatus.initial) {
      final photos = await _openAlbum.call(
        QueryParameters(
          0,
          _photoLimit,
          albumId: event.albumId,
        ),
      );
      return emit(
        photos.fold(
          (value) => state.copyWith(
            status: FetchStatus.success,
            photos: value,
            hasReachedMax: false,
          ),
          (error) => state.copyWith(
            status: FetchStatus.failure,
            photos: [],
            hasReachedMax: false,
          ),
        ),
      );
    }
    final photos = await _openAlbum.call(
      QueryParameters(
        state.photos.length,
        _photoLimit,
        albumId: event.albumId,
      ),
    );
    return emit(
      photos.fold(
        (value) {
          if (value.isEmpty) {
            return state.copyWith(
              hasReachedMax: true,
            );
          }
          return state.copyWith(
            status: FetchStatus.success,
            photos: List.of(state.photos)..addAll(value),
            hasReachedMax: false,
          );
        },
        (error) => state.copyWith(
          status: FetchStatus.failure,
          photos: List.of(state.photos),
          hasReachedMax: false,
        ),
      ),
    );
  }
}
