import 'package:built_value/json_object.dart';
import 'package:latlong2/latlong.dart';

import '../domain/place_models.dart';
import 'graphql/__generated__/add_favorite_place.data.gql.dart';
import 'graphql/__generated__/authenticate_telegram.data.gql.dart';
import 'graphql/__generated__/authenticate_telegram_login.data.gql.dart';
import 'graphql/__generated__/complete_telegram_bot_login.data.gql.dart';
import 'graphql/__generated__/favorite_places.data.gql.dart';
import 'graphql/__generated__/me.data.gql.dart';
import 'graphql/__generated__/nearby_places.data.gql.dart';
import 'graphql/__generated__/place.data.gql.dart';
import 'graphql/__generated__/places.data.gql.dart';
import 'graphql/__generated__/recommend_places_by_voice.data.gql.dart';
import 'graphql/__generated__/remove_favorite_place.data.gql.dart';
import 'graphql/__generated__/voice_experiences.data.gql.dart';

AppUser appUserFromMe(GMeData_me user) {
  return _appUser(
    id: user.id,
    telegramId: user.telegramId,
    username: user.username,
    firstName: user.firstName,
    lastName: user.lastName,
    photoUrl: user.photoUrl,
    languageCode: user.languageCode,
    isAdmin: user.isAdmin,
  );
}

AppUser appUserFromAuthenticateTelegram(
  GAuthenticateTelegramData_authenticateTelegram_user user,
) {
  return _appUser(
    id: user.id,
    telegramId: user.telegramId,
    username: user.username,
    firstName: user.firstName,
    lastName: user.lastName,
    photoUrl: user.photoUrl,
    languageCode: user.languageCode,
    isAdmin: user.isAdmin,
  );
}

AppUser appUserFromAuthenticateTelegramLogin(
  GAuthenticateTelegramLoginData_authenticateTelegramLogin_user user,
) {
  return _appUser(
    id: user.id,
    telegramId: user.telegramId,
    username: user.username,
    firstName: user.firstName,
    lastName: user.lastName,
    photoUrl: user.photoUrl,
    languageCode: user.languageCode,
    isAdmin: user.isAdmin,
  );
}

AppUser appUserFromCompleteTelegramBotLogin(
  GCompleteTelegramBotLoginData_completeTelegramBotLogin_user user,
) {
  return _appUser(
    id: user.id,
    telegramId: user.telegramId,
    username: user.username,
    firstName: user.firstName,
    lastName: user.lastName,
    photoUrl: user.photoUrl,
    languageCode: user.languageCode,
    isAdmin: user.isAdmin,
  );
}

AppUser _appUser({
  required String id,
  required String telegramId,
  required String? username,
  required String? firstName,
  required String? lastName,
  required String? photoUrl,
  required String? languageCode,
  required bool isAdmin,
}) {
  return AppUser(
    id: id,
    telegramId: telegramId,
    username: username,
    firstName: firstName,
    lastName: lastName,
    photoUrl: photoUrl,
    languageCode: languageCode,
    isAdmin: isAdmin,
  );
}

PlaceRecommendation placeRecommendationFromPlace(GPlacesData_places place) {
  return PlaceRecommendation(
    id: place.id,
    googlePlaceId: place.googlePlaceId,
    name: place.name,
    area: '',
    photoUrls: const [],
    coordinate: LatLng(place.latitude, place.longitude),
    traits: _traitsFromTags(place.traits),
    googlePrimaryType: place.googlePrimaryType,
    googleTypes: place.googleTypes.toList().cast<String>(),
    googleBusinessStatus: null,
    googleRating: null,
    googleUserRatingCount: null,
    googleRegularOpeningHours: null,
    googleCurrentOpeningHours: null,
    isFavorite: place.isFavorite,
  );
}

PlaceRecommendation placeRecommendationFromPlaceDetails(
  GPlaceData_place place,
) {
  return PlaceRecommendation(
    id: place.id,
    googlePlaceId: place.googlePlaceId,
    name: place.name,
    area: '',
    photoUrls: place.photoUrls.toList().cast<String>(),
    coordinate: LatLng(place.latitude, place.longitude),
    traits: _traitsFromTags(place.traits),
    googlePrimaryType: place.googlePrimaryType,
    googleTypes: place.googleTypes.toList().cast<String>(),
    googleBusinessStatus: null,
    googleRating: null,
    googleUserRatingCount: null,
    googleRegularOpeningHours: _jsonMap(place.googleRegularOpeningHours),
    googleCurrentOpeningHours: _jsonMap(place.googleCurrentOpeningHours),
    isFavorite: place.isFavorite,
  );
}

PlaceRecommendation placeRecommendationFromNearbyPlace(
  GNearbyPlacesData_nearbyPlaces place,
) {
  return PlaceRecommendation(
    id: place.id,
    googlePlaceId: place.googlePlaceId,
    name: place.name,
    area: '',
    photoUrls: place.photoUrls.toList().cast<String>(),
    coordinate: LatLng(place.latitude, place.longitude),
    traits: _traitsFromTags(place.traits),
    googlePrimaryType: place.googlePrimaryType,
    googleTypes: place.googleTypes.toList().cast<String>(),
    googleBusinessStatus: place.googleBusinessStatus,
    googleRating: place.googleRating,
    googleUserRatingCount: place.googleUserRatingCount,
    googleRegularOpeningHours: _jsonMap(place.googleRegularOpeningHours),
    googleCurrentOpeningHours: _jsonMap(place.googleCurrentOpeningHours),
    isFavorite: place.isFavorite,
  );
}

PlaceRecommendation placeRecommendationFromFavoritePlace(
  GFavoritePlacesData_favoritePlaces place,
) {
  return PlaceRecommendation(
    id: place.id,
    googlePlaceId: place.googlePlaceId,
    name: place.name,
    area: '',
    photoUrls: place.photoUrls.toList().cast<String>(),
    coordinate: LatLng(place.latitude, place.longitude),
    traits: _traitsFromTags(place.traits),
    googlePrimaryType: place.googlePrimaryType,
    googleTypes: place.googleTypes.toList().cast<String>(),
    googleBusinessStatus: place.googleBusinessStatus,
    googleRating: place.googleRating,
    googleUserRatingCount: place.googleUserRatingCount,
    googleRegularOpeningHours: _jsonMap(place.googleRegularOpeningHours),
    googleCurrentOpeningHours: _jsonMap(place.googleCurrentOpeningHours),
    isFavorite: place.isFavorite,
  );
}

PlaceRecommendation placeRecommendationFromVoiceRecommendedPlace(
  GRecommendPlacesByVoiceData_recommendPlacesByVoice_places place,
) {
  return PlaceRecommendation(
    id: place.id,
    googlePlaceId: place.googlePlaceId,
    name: place.name,
    area: '',
    photoUrls: place.photoUrls.toList().cast<String>(),
    coordinate: LatLng(place.latitude, place.longitude),
    traits: _traitsFromTags(place.traits),
    googlePrimaryType: place.googlePrimaryType,
    googleTypes: place.googleTypes.toList().cast<String>(),
    googleBusinessStatus: place.googleBusinessStatus,
    googleRating: place.googleRating,
    googleUserRatingCount: place.googleUserRatingCount,
    googleRegularOpeningHours: _jsonMap(place.googleRegularOpeningHours),
    googleCurrentOpeningHours: _jsonMap(place.googleCurrentOpeningHours),
    isFavorite: place.isFavorite,
  );
}

PlaceRecommendation placeRecommendationFromAddedFavorite(
  GAddFavoritePlaceData_addFavoritePlace place,
) {
  return PlaceRecommendation(
    id: place.id,
    googlePlaceId: place.googlePlaceId,
    name: place.name,
    area: '',
    photoUrls: place.photoUrls.toList().cast<String>(),
    coordinate: LatLng(place.latitude, place.longitude),
    traits: _traitsFromTags(place.traits),
    googlePrimaryType: place.googlePrimaryType,
    googleTypes: place.googleTypes.toList().cast<String>(),
    googleBusinessStatus: place.googleBusinessStatus,
    googleRating: place.googleRating,
    googleUserRatingCount: place.googleUserRatingCount,
    googleRegularOpeningHours: _jsonMap(place.googleRegularOpeningHours),
    googleCurrentOpeningHours: _jsonMap(place.googleCurrentOpeningHours),
    isFavorite: place.isFavorite,
  );
}

PlaceRecommendation placeRecommendationFromRemovedFavorite(
  GRemoveFavoritePlaceData_removeFavoritePlace place,
) {
  return PlaceRecommendation(
    id: place.id,
    googlePlaceId: place.googlePlaceId,
    name: place.name,
    area: '',
    photoUrls: place.photoUrls.toList().cast<String>(),
    coordinate: LatLng(place.latitude, place.longitude),
    traits: _traitsFromTags(place.traits),
    googlePrimaryType: place.googlePrimaryType,
    googleTypes: place.googleTypes.toList().cast<String>(),
    googleBusinessStatus: place.googleBusinessStatus,
    googleRating: place.googleRating,
    googleUserRatingCount: place.googleUserRatingCount,
    googleRegularOpeningHours: _jsonMap(place.googleRegularOpeningHours),
    googleCurrentOpeningHours: _jsonMap(place.googleCurrentOpeningHours),
    isFavorite: place.isFavorite,
  );
}

VoiceExperienceDebug voiceExperienceDebugFromVoiceExperience(
  GVoiceExperiencesData_voiceExperiences experience,
) {
  return VoiceExperienceDebug(
    id: experience.id,
    placeName: experience.place.name,
    userName: _userDisplayName(experience.user),
    status: experience.status.name,
    durationSeconds: experience.durationSeconds,
    transcript: experience.transcript,
    analysis: _jsonMap(experience.analysis),
    createdAt: DateTime.parse(experience.createdAt),
  );
}

String _userDisplayName(GVoiceExperiencesData_voiceExperiences_user? user) {
  if (user == null) {
    return '';
  }

  final firstName = user.firstName;
  final username = user.username;
  final telegramId = user.telegramId;
  if (firstName != null && firstName.trim().isNotEmpty) {
    return firstName;
  }
  if (username != null && username.trim().isNotEmpty) {
    return '@$username';
  }
  return telegramId;
}

Set<PlaceTrait> _traitsFromTags(Iterable<String> tags) {
  final traits = <PlaceTrait>{};
  for (final tag in tags) {
    final trait = _traitByTag(tag);
    if (trait != null) {
      traits.add(trait);
    }
  }

  return traits;
}

Map<String, dynamic>? _jsonMap(JsonObject? object) {
  if (object == null || !object.isMap) {
    return null;
  }

  return object.asMap.map((key, value) => MapEntry(key.toString(), value));
}

PlaceTrait? _traitByTag(String tag) {
  final name = tag.contains(':') ? tag.split(':').last : tag;
  for (final trait in PlaceTrait.values) {
    if (trait.name == name) {
      return trait;
    }
  }
  return null;
}
