import 'dart:convert'; import 'package:drift/drift.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../core/api/api_client.dart'; import '../../../core/database/app_database.dart'; /// A POI parsed from the API response. class PlaceData { final int osmId; final String osmType; final String name; final String category; final double latitude; final double longitude; final Map? address; final String? openingHours; final Map? openingHoursParsed; final String? phone; final String? website; final String? wheelchair; final Map? tags; PlaceData({ required this.osmId, required this.osmType, required this.name, required this.category, required this.latitude, required this.longitude, this.address, this.openingHours, this.openingHoursParsed, this.phone, this.website, this.wheelchair, this.tags, }); String get displayAddress { if (address == null) return ''; final parts = []; final street = address!['street'] as String?; final housenumber = address!['housenumber'] as String?; if (street != null) { parts.add(housenumber != null ? '$street $housenumber' : street); } final postcode = address!['postcode'] as String?; final city = address!['city'] as String?; if (postcode != null && city != null) { parts.add('$postcode $city'); } else if (city != null) { parts.add(city); } return parts.join(', '); } factory PlaceData.fromGeoJsonFeature(Map feature) { final geometry = feature['geometry'] as Map; final coords = geometry['coordinates'] as List; final props = feature['properties'] as Map; return PlaceData( osmId: props['osm_id'] as int, osmType: props['osm_type'] as String, name: props['name'] as String? ?? '', category: props['category'] as String? ?? 'unknown', longitude: (coords[0] as num).toDouble(), latitude: (coords[1] as num).toDouble(), address: props['address'] as Map?, openingHours: props['opening_hours'] as String?, openingHoursParsed: props['opening_hours_parsed'] as Map?, phone: props['phone'] as String?, website: props['website'] as String?, wheelchair: props['wheelchair'] as String?, tags: props['tags'] as Map?, ); } /// Parse a single Feature response (GET /api/pois/{osm_type}/{osm_id}). factory PlaceData.fromGeoJsonSingle(Map data) { return PlaceData.fromGeoJsonFeature(data); } } class PlacesRepository { final ApiClient _apiClient; final AppDatabase _db; PlacesRepository(this._apiClient, this._db); /// Get POIs in a bounding box via GET /api/pois?bbox=... Future> getPoisInBbox({ required double minLon, required double minLat, required double maxLon, required double maxLat, String? category, int limit = 100, }) async { final params = { 'bbox': '$minLon,$minLat,$maxLon,$maxLat', 'limit': limit, }; if (category != null) { params['category'] = category; } final data = await _apiClient.get('/api/pois', queryParameters: params); final features = (data['features'] as List?) ?? []; return features .map((f) => PlaceData.fromGeoJsonFeature(f as Map)) .toList(); } /// Get a single POI by type and ID via GET /api/pois/{osm_type}/{osm_id}. Future getPoiDetail(String osmType, int osmId) async { final data = await _apiClient.get('/api/pois/$osmType/$osmId'); return PlaceData.fromGeoJsonSingle(data as Map); } // ----------------------------------------------------------------------- // Favorites (local Drift DB) // ----------------------------------------------------------------------- Stream> watchFavorites() { return _db.watchAllFavorites(); } Future> getAllFavorites() { return _db.getAllFavorites(); } Future addFavorite(PlaceData place) { return _db.addFavorite(FavoritesCompanion.insert( osmId: Value(place.osmId), osmType: Value(place.osmType), name: place.name, latitude: place.latitude, longitude: place.longitude, addressJson: Value( place.address != null ? jsonEncode(place.address) : null, ), createdAt: DateTime.now().millisecondsSinceEpoch ~/ 1000, updatedAt: DateTime.now().millisecondsSinceEpoch ~/ 1000, )); } Future removeFavorite(int id) { return _db.deleteFavorite(id); } Future isFavorited(String osmType, int osmId) { return _db.findFavoriteByOsm(osmType, osmId); } } final placesRepositoryProvider = Provider((ref) { return PlacesRepository( ref.watch(apiClientProvider), ref.watch(appDatabaseProvider), ); });