import 'package:latlong2/latlong.dart';

import '../domain/place_models.dart';
import 'graphql/__generated__/add_favorite_place.req.gql.dart';
import 'graphql/__generated__/favorite_places.req.gql.dart';
import 'graphql/__generated__/nearby_places.req.gql.dart';
import 'graphql/__generated__/place.req.gql.dart';
import 'graphql/__generated__/places.req.gql.dart';
import 'graphql/__generated__/recommend_places_by_voice.req.gql.dart';
import 'graphql/__generated__/remove_favorite_place.req.gql.dart';
import 'mapflow_data_mappers.dart';
import 'mapflow_graphql_client.dart';

class PlacesRepository {
  const PlacesRepository({required MapflowGraphqlClient client})
    : _client = client;

  final MapflowGraphqlClient _client;

  Future<List<PlaceRecommendation>> fetchPlaces() async {
    final data = await _client.request(GPlacesReq());
    return data.places.map(placeRecommendationFromPlace).toList();
  }

  Future<PlaceRecommendation> fetchPlace(String placeId) async {
    final data = await _client.request(
      GPlaceReq((b) {
        b.vars.id = placeId;
      }),
    );
    return placeRecommendationFromPlaceDetails(data.place);
  }

  Future<List<PlaceRecommendation>> fetchFavoritePlaces() async {
    final data = await _client.request(GFavoritePlacesReq());
    return data.favoritePlaces
        .map(placeRecommendationFromFavoritePlace)
        .toList();
  }

  Future<List<PlaceRecommendation>> fetchNearbyPlaces({
    required LatLng coordinate,
    required int radiusMeters,
  }) async {
    final data = await _client.request(
      GNearbyPlacesReq((b) {
        b.vars.input
          ..latitude = coordinate.latitude
          ..longitude = coordinate.longitude
          ..radiusMeters = radiusMeters;
      }),
    );
    return data.nearbyPlaces.map(placeRecommendationFromNearbyPlace).toList();
  }

  Future<VoicePlaceRecommendationResult> recommendPlacesByVoice({
    required String audioContentBase64,
    required String audioMimeType,
  }) async {
    final data = await _client.request(
      GRecommendPlacesByVoiceReq((b) {
        b.vars.input
          ..audioContentBase64 = audioContentBase64
          ..audioMimeType = audioMimeType;
      }),
    );
    return VoicePlaceRecommendationResult(
      transcript: data.recommendPlacesByVoice.transcript,
      tags: data.recommendPlacesByVoice.tags.toList().cast<String>(),
      summary: data.recommendPlacesByVoice.summary,
      places: data.recommendPlacesByVoice.places
          .map(placeRecommendationFromVoiceRecommendedPlace)
          .toList(),
    );
  }

  Future<PlaceRecommendation> addFavoritePlace(String placeId) async {
    final data = await _client.request(
      GAddFavoritePlaceReq((b) {
        b.vars.placeId = placeId;
      }),
    );
    return placeRecommendationFromAddedFavorite(data.addFavoritePlace);
  }

  Future<PlaceRecommendation> removeFavoritePlace(String placeId) async {
    final data = await _client.request(
      GRemoveFavoritePlaceReq((b) {
        b.vars.placeId = placeId;
      }),
    );
    return placeRecommendationFromRemovedFavorite(data.removeFavoritePlace);
  }
}
