import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:lunchpick/domain/entities/restaurant.dart'; import 'package:lunchpick/domain/repositories/restaurant_repository.dart'; import 'package:lunchpick/presentation/providers/di_providers.dart'; import 'package:uuid/uuid.dart'; /// 맛집 목록 Provider final restaurantListProvider = StreamProvider>((ref) { final repository = ref.watch(restaurantRepositoryProvider); return repository.watchRestaurants(); }); /// 특정 맛집 Provider final restaurantProvider = FutureProvider.family((ref, id) async { final repository = ref.watch(restaurantRepositoryProvider); return repository.getRestaurantById(id); }); /// 카테고리 목록 Provider final categoriesProvider = FutureProvider>((ref) async { final repository = ref.watch(restaurantRepositoryProvider); return repository.getAllCategories(); }); /// 맛집 관리 StateNotifier class RestaurantNotifier extends StateNotifier> { final RestaurantRepository _repository; RestaurantNotifier(this._repository) : super(const AsyncValue.data(null)); /// 맛집 추가 Future addRestaurant({ required String name, required String category, required String subCategory, String? description, String? phoneNumber, required String roadAddress, required String jibunAddress, required double latitude, required double longitude, required DataSource source, }) async { state = const AsyncValue.loading(); try { final restaurant = Restaurant( id: const Uuid().v4(), name: name, category: category, subCategory: subCategory, description: description, phoneNumber: phoneNumber, roadAddress: roadAddress, jibunAddress: jibunAddress, latitude: latitude, longitude: longitude, source: source, createdAt: DateTime.now(), updatedAt: DateTime.now(), ); await _repository.addRestaurant(restaurant); state = const AsyncValue.data(null); } catch (e, stack) { state = AsyncValue.error(e, stack); } } /// 맛집 수정 Future updateRestaurant(Restaurant restaurant) async { state = const AsyncValue.loading(); try { final updated = Restaurant( id: restaurant.id, name: restaurant.name, category: restaurant.category, subCategory: restaurant.subCategory, description: restaurant.description, phoneNumber: restaurant.phoneNumber, roadAddress: restaurant.roadAddress, jibunAddress: restaurant.jibunAddress, latitude: restaurant.latitude, longitude: restaurant.longitude, lastVisitDate: restaurant.lastVisitDate, source: restaurant.source, createdAt: restaurant.createdAt, updatedAt: DateTime.now(), ); await _repository.updateRestaurant(updated); state = const AsyncValue.data(null); } catch (e, stack) { state = AsyncValue.error(e, stack); } } /// 맛집 삭제 Future deleteRestaurant(String id) async { state = const AsyncValue.loading(); try { await _repository.deleteRestaurant(id); state = const AsyncValue.data(null); } catch (e, stack) { state = AsyncValue.error(e, stack); } } /// 마지막 방문일 업데이트 Future updateLastVisitDate(String restaurantId, DateTime visitDate) async { try { await _repository.updateLastVisitDate(restaurantId, visitDate); } catch (e, stack) { state = AsyncValue.error(e, stack); } } /// 네이버 지도 URL로부터 맛집 추가 Future addRestaurantFromUrl(String url) async { state = const AsyncValue.loading(); try { final restaurant = await _repository.addRestaurantFromUrl(url); state = const AsyncValue.data(null); return restaurant; } catch (e, stack) { state = AsyncValue.error(e, stack); rethrow; } } /// 미리 생성된 Restaurant 객체를 직접 추가 Future addRestaurantDirect(Restaurant restaurant) async { state = const AsyncValue.loading(); try { await _repository.addRestaurant(restaurant); state = const AsyncValue.data(null); } catch (e, stack) { state = AsyncValue.error(e, stack); rethrow; } } } /// RestaurantNotifier Provider final restaurantNotifierProvider = StateNotifierProvider>((ref) { final repository = ref.watch(restaurantRepositoryProvider); return RestaurantNotifier(repository); }); /// 거리 내 맛집 Provider final restaurantsWithinDistanceProvider = FutureProvider.family, ({double latitude, double longitude, double maxDistance})>((ref, params) async { final repository = ref.watch(restaurantRepositoryProvider); return repository.getRestaurantsWithinDistance( userLatitude: params.latitude, userLongitude: params.longitude, maxDistanceInMeters: params.maxDistance, ); }); /// n일 이내 방문하지 않은 맛집 Provider final restaurantsNotVisitedInDaysProvider = FutureProvider.family, int>((ref, days) async { final repository = ref.watch(restaurantRepositoryProvider); return repository.getRestaurantsNotVisitedInDays(days); }); /// 검색어로 맛집 검색 Provider final searchRestaurantsProvider = FutureProvider.family, String>((ref, query) async { final repository = ref.watch(restaurantRepositoryProvider); return repository.searchRestaurants(query); }); /// 카테고리별 맛집 Provider final restaurantsByCategoryProvider = FutureProvider.family, String>((ref, category) async { final repository = ref.watch(restaurantRepositoryProvider); return repository.getRestaurantsByCategory(category); }); /// 검색 쿼리 상태 Provider final searchQueryProvider = StateProvider((ref) => ''); /// 선택된 카테고리 상태 Provider final selectedCategoryProvider = StateProvider((ref) => null); /// 필터링된 맛집 목록 Provider (검색 + 카테고리) final filteredRestaurantsProvider = StreamProvider>((ref) async* { final searchQuery = ref.watch(searchQueryProvider); final selectedCategory = ref.watch(selectedCategoryProvider); final restaurantsStream = ref.watch(restaurantListProvider.stream); await for (final restaurants in restaurantsStream) { var filtered = restaurants; // 검색 필터 적용 if (searchQuery.isNotEmpty) { final lowercaseQuery = searchQuery.toLowerCase(); filtered = filtered.where((restaurant) { return restaurant.name.toLowerCase().contains(lowercaseQuery) || (restaurant.description?.toLowerCase().contains(lowercaseQuery) ?? false) || restaurant.category.toLowerCase().contains(lowercaseQuery); }).toList(); } // 카테고리 필터 적용 if (selectedCategory != null) { filtered = filtered.where((restaurant) { return restaurant.category == selectedCategory; }).toList(); } yield filtered; } });