// lib/features/events/services/events_service.dart import '../../../core/api/api_client.dart'; import '../../../core/api/api_endpoints.dart'; import '../models/event_models.dart'; class EventsService { final ApiClient _api = ApiClient(); // --------------------------------------------------------------------------- // In-memory caches with TTL // --------------------------------------------------------------------------- static List? _cachedTypes; static DateTime? _typesCacheTime; static const _typesCacheTTL = Duration(minutes: 30); static List? _cachedAllEvents; static DateTime? _eventsCacheTime; static const _eventsCacheTTL = Duration(minutes: 5); /// Get event types (POST to /events/type-list/) /// Cached for 30 minutes since event types rarely change. Future> getEventTypes() async { if (_cachedTypes != null && _typesCacheTime != null && DateTime.now().difference(_typesCacheTime!) < _typesCacheTTL) { return _cachedTypes!; } final res = await _api.post(ApiEndpoints.eventTypes, requiresAuth: false); final list = []; final data = res['event_types'] ?? res; if (data is List) { for (final e in data) { if (e is Map) list.add(EventTypeModel.fromJson(e)); } } _cachedTypes = list; _typesCacheTime = DateTime.now(); return list; } /// Get events filtered by pincode with pagination. /// [page] starts at 1. [pageSize] defaults to 50. /// Returns a list of events for the requested page. Future> getEventsByPincode(String pincode, {int page = 1, int pageSize = 50, int perType = 5}) async { // Use cache for 'all' pincode queries (first page only for initial load) if (pincode == 'all' && page == 1 && _cachedAllEvents != null && _eventsCacheTime != null && DateTime.now().difference(_eventsCacheTime!) < _eventsCacheTTL) { return _cachedAllEvents!; } final Map body = {'pincode': pincode, 'page': page, 'page_size': pageSize}; // Diverse mode: fetch a few events per type so all categories are represented if (perType > 0 && page == 1) body['per_type'] = perType; final res = await _api.post( ApiEndpoints.eventsByPincode, body: body, requiresAuth: false, ); final list = []; final events = res['events'] ?? res['data'] ?? []; if (events is List) { for (final e in events) { if (e is Map) list.add(EventModel.fromJson(Map.from(e))); } } if (pincode == 'all' && page == 1) { _cachedAllEvents = list; _eventsCacheTime = DateTime.now(); } return list; } /// Event details — requiresAuth: false so guests can fetch full details Future getEventDetails(int eventId) async { final res = await _api.post(ApiEndpoints.eventDetails, body: {'event_id': eventId}, requiresAuth: false); return EventModel.fromJson(Map.from(res)); } /// Related events by event_type_id (EVT-002). /// Fetches events with the same category, silently returns [] on failure. Future> getEventsByCategory(int eventTypeId, {int limit = 5}) async { try { final res = await _api.post( ApiEndpoints.eventsByCategory, body: {'event_type_id': eventTypeId, 'page_size': limit, 'page': 1}, requiresAuth: false, ); final results = res['results'] ?? res['events'] ?? res['data'] ?? []; if (results is List) { return results .whereType>() .map((e) => EventModel.fromJson(e)) .toList(); } } catch (_) { // silently fail — related events are non-critical } return []; } /// Events by month and year for calendar (POST to /events/events-by-month-year/) Future> getEventsByMonthYear(String month, int year) async { final res = await _api.post(ApiEndpoints.eventsByMonth, body: {'month': month, 'year': year}, requiresAuth: false); return res; } /// Convenience: get events for a specific date (YYYY-MM-DD). /// Uses the cached events list when available to avoid redundant API calls. Future> getEventsForDate(String date) async { final all = await getEventsByPincode('all'); return all.where((e) { try { return e.startDate == date || e.endDate == date || (DateTime.parse(e.startDate).isBefore(DateTime.parse(date)) && DateTime.parse(e.endDate).isAfter(DateTime.parse(date))); } catch (_) { return false; } }).toList(); } }