import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/foundation.dart'; import 'friendship_service.dart'; import 'handle_validator.dart'; import 'rider_profile_models.dart'; import 'rider_profile_service.dart'; /// Search service for discovering riders by unique handle while respecting privacy controls. class RiderSearchService { final FirebaseFirestore? _firestore; final RiderProfileService _profileService; final FriendshipService _friendshipService; RiderSearchService({ FirebaseFirestore? firestore, RiderProfileService? profileService, FriendshipService? friendshipService, }) : _firestore = firestore ?? _resolveFirestore(), _profileService = profileService ?? RiderProfileService(firestore: firestore), _friendshipService = friendshipService ?? FriendshipService(firestore: firestore); static FirebaseFirestore? _resolveFirestore() { try { if (Firebase.apps.isNotEmpty) return FirebaseFirestore.instance; } catch (_) {} return null; } /// Searches for a rider by exact or normalized handle. /// Returns null if not found, blocked, or hidden by privacy settings. Future searchByHandle({ required String rawHandle, required String callerUid, }) async { final validation = HandleValidator.validate(rawHandle); if (!validation.isValid) return null; final normalized = validation.normalizedHandle!; final firestore = _firestore; if (firestore == null) return null; try { // 1. Resolve handle to UID final handleDoc = await firestore.collection('handles').doc(normalized).get(); if (!handleDoc.exists || handleDoc.data() == null) return null; final targetUid = handleDoc.data()!['uid'] as String?; if (targetUid == null || targetUid.isEmpty) return null; // 2. Self check if (targetUid == callerUid) { final profile = await _profileService.getProfile(callerUid); if (profile == null) return null; return RiderSearchResult( uid: profile.uid, handle: profile.handle, displayName: profile.displayName, bio: profile.bio, region: profile.region, avatarPreset: profile.avatarPreset, avatarUrl: profile.avatarUrl, isProfilePublic: profile.privacySettings.isProfilePublic, allowFriendRequests: profile.privacySettings.allowFriendRequests, relationship: FriendshipStatus.self, ); } // 3. Check blocks between caller and target final callerBlockDoc = await firestore .collection('users') .doc(callerUid) .collection('blocks') .doc(targetUid) .get(); if (callerBlockDoc.exists) return null; final targetBlockDoc = await firestore .collection('users') .doc(targetUid) .collection('blocks') .doc(callerUid) .get(); if (targetBlockDoc.exists) return null; // 4. Fetch target profile final targetProfile = await _profileService.getProfile(targetUid); if (targetProfile == null) return null; // 5. Determine relationship final relationship = await _friendshipService.getRelationshipStatus( callerUid, targetUid, ); // 6. Respect handle searchability privacy setting final isFriend = relationship == FriendshipStatus.friend; final hasPending = relationship == FriendshipStatus.requestIncoming || relationship == FriendshipStatus.requestOutgoing; if (!targetProfile.privacySettings.isSearchableByHandle && !isFriend && !hasPending) { return null; } // 7. Respect public/private profile setting final isPublic = targetProfile.privacySettings.isProfilePublic; final showFullProfile = isPublic || isFriend; return RiderSearchResult( uid: targetProfile.uid, handle: targetProfile.handle, displayName: targetProfile.displayName, bio: showFullProfile ? targetProfile.bio : '', region: showFullProfile ? targetProfile.region : '', avatarPreset: targetProfile.avatarPreset, avatarUrl: targetProfile.avatarUrl, isProfilePublic: isPublic, allowFriendRequests: targetProfile.privacySettings.allowFriendRequests, relationship: relationship, ); } catch (e) { debugPrint('[RiderSearchService] Search error for $normalized: $e'); return null; } } }