import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/models/marketplace.dart'; import '../../../core/services/marketplace_service.dart'; final marketplaceServiceProvider = Provider((_) => MarketplaceService()); final citiesProvider = FutureProvider>((ref) { return ref.read(marketplaceServiceProvider).getCities(); }); final offersProvider = FutureProvider>((ref) { return ref.read(marketplaceServiceProvider).getOffers(); }); class SearchParams { final String? city; final DateTime? startDate; final DateTime? endDate; final Set? categories; // null = all; multi-select final String? transmission; final int? maxPriceCents; final int? minPriceCents; // client-side filter only final int page; const SearchParams({ this.city, this.startDate, this.endDate, this.categories, this.transmission, this.maxPriceCents, this.minPriceCents, this.page = 1, }); bool get hasDateRange => startDate != null && endDate != null; bool get hasFilters => city != null || (categories != null && categories!.isNotEmpty) || transmission != null || maxPriceCents != null || minPriceCents != null || hasDateRange; SearchParams copyWith({ Object? city = _sentinel, DateTime? startDate, DateTime? endDate, Object? categories = _sentinel, Object? transmission = _sentinel, Object? maxPriceCents = _sentinel, Object? minPriceCents = _sentinel, int? page, bool clearDates = false, }) => SearchParams( city: city == _sentinel ? this.city : city as String?, startDate: clearDates ? null : (startDate ?? this.startDate), endDate: clearDates ? null : (endDate ?? this.endDate), categories: categories == _sentinel ? this.categories : categories as Set?, transmission: transmission == _sentinel ? this.transmission : transmission as String?, maxPriceCents: maxPriceCents == _sentinel ? this.maxPriceCents : maxPriceCents as int?, minPriceCents: minPriceCents == _sentinel ? this.minPriceCents : minPriceCents as int?, page: page ?? this.page, ); static bool _setsEqual(Set? a, Set? b) { if (a == null && b == null) return true; if (a == null || b == null) return false; if (a.length != b.length) return false; return a.containsAll(b); } @override bool operator ==(Object other) => other is SearchParams && other.city == city && other.startDate == startDate && other.endDate == endDate && _setsEqual(other.categories, categories) && other.transmission == transmission && other.maxPriceCents == maxPriceCents && other.minPriceCents == minPriceCents && other.page == page; @override int get hashCode { final catKey = categories == null ? null : (categories!.toList()..sort()).join(','); return Object.hash(city, startDate, endDate, catKey, transmission, maxPriceCents, minPriceCents, page); } } const _sentinel = Object(); final searchParamsProvider = StateProvider((_) { final now = DateTime.now(); return SearchParams( startDate: now, endDate: now.add(const Duration(days: 1)), ); }); final vehicleSearchProvider = FutureProvider.family( (ref, params) { // Only send a single category to the API; multi-category is filtered client-side final apiCategory = params.categories?.length == 1 ? params.categories!.first : null; return ref.read(marketplaceServiceProvider).search( city: params.city, startDate: params.startDate, endDate: params.endDate, category: apiCategory, transmission: params.transmission, maxPriceCents: params.maxPriceCents, page: params.page, ); });