// 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/domain/data_sources/local/favorite_local_data_source.dart';
import 'package:architecture_example/domain/data_sources/remote/album_remote_data_source.dart';
import 'package:architecture_example/domain/data_sources/remote/photo_remote_data_source.dart';
import 'package:architecture_example/domain/entities/album.dart';
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';

class PhotoRepositoryImpl extends PhotoRepository {

  PhotoRepositoryImpl(
    this._photoRemoteDataSource,
    this._albumRemoteDataSource,
    this._favoriteLocalDataSource,
  );
  final PhotoRemoteDataSource _photoRemoteDataSource;
  final AlbumRemoteDataSource _albumRemoteDataSource;
  final FavoriteLocalDataSource _favoriteLocalDataSource;

  @override
  FutureOrResult<void> addPhotoToFavorites(Photo photo) => Result.tryCatchAsync(
        () => _favoriteLocalDataSource.addPhotoToFavorites(photo),
        (error) => ClientException('Cannot add photo to favorites.'),
      );

  @override
  FutureOrResult<bool> checkIfPhotoIsInFavorites(int id) =>
      Result.tryCatchAsync(
        () => _favoriteLocalDataSource.checkIfPhotoIsInFavorites(id),
        (error) =>
            ClientException('Cannot check if photo `$id` is in favorites.'),
      );

  @override
  FutureOrResult<void> deletePhotoFromFavorites(int id) => Result.tryCatchAsync(
        () => _favoriteLocalDataSource.deletePhotoFromFavorites(id),
        (error) => ClientException('Cannot delete photo `$id` from favorites.'),
      );

  @override
  FutureOrResult<Album> getAlbum(int id) => Result.tryCatchAsync(
        () => _albumRemoteDataSource.getAlbum(id),
        (error) => ServerException('Cannot retrieve album $id.'),
      );

  @override
  FutureOrResult<List<Album>> getAllAlbums({int? start, int? limit}) =>
      Result.tryCatchAsync(
        () => _albumRemoteDataSource.getAllAlbums(start: start, limit: limit),
        (error) => ServerException('Cannot retrieve all albums.'),
      );

  @override
  FutureOrResult<List<Photo>> getAllPhotos({int? start, int? limit}) async =>
      Result.tryCatchAsync(
        () => _photoRemoteDataSource.getAllPhotos(start: start, limit: limit),
        (error) => ServerException('Cannot retrieve all photos.'),
      );

  @override
  FutureOrResult<List<Photo>> getAllPhotosFromFavorites() async {
    try {
      final response = <Photo>[];
      final favorites =
          await _favoriteLocalDataSource.getAllPhotosFromFavorites();
      final photos = await _photoRemoteDataSource.getAllPhotos();

      for (final photo in photos) {
        if (favorites.any((favId) => photo.id == favId)) {
          response.add(photo);
        }
      }
      return Ok(response);
    } catch (_) {
      return Err(
        ClientException('Cannot retrieve all photos from favorites.'),
      );
    }
  }

  @override
  FutureOrResult<Photo> getPhoto(int id) => Result.tryCatchAsync(
        () => _photoRemoteDataSource.getPhoto(id),
        (error) => ServerException('Cannot retrieve photo $id.'),
      );

  @override
  FutureOrResult<List<Photo>> getPhotosFromAlbum(
    int albumId, {
    int? start,
    int? limit,
  }) async =>
      Result.tryCatchAsync(
        () => _photoRemoteDataSource.getPhotosFromAlbum(
          albumId,
          start: start,
          limit: limit,
        ),
        (error) =>
            ServerException('Cannot retrieve all photos from album $albumId'),
      );
}
