import {QueryDocumentSnapshot, Timestamp} from "firebase-admin/firestore"; import {cleanEntityId} from "../../core/data/entities/entity_utils"; export type RideStatus = | "requested" | "accepted" | "arriving" | "in_progress" | "completed" | "canceled"; export interface RideEntityData { id?: string; passengerId: string; driverId?: string; status: RideStatus; pickupLat: number; pickupLng: number; pickupAddress: string; /** Geohash of the pickup point — lets a driver look up nearby requests * without a Firestore rule allowing them to read every ride in the app. */ pickupGeohash?: string; dropoffLat: number; dropoffLng: number; dropoffAddress: string; estimatedDistanceM: number; estimatedDurationS: number; estimatedPrice: number; currency: string; requestedAt: Timestamp; acceptedAt?: Timestamp; startedAt?: Timestamp; completedAt?: Timestamp; canceledAt?: Timestamp; cancelReason?: string; passengerRating?: number; driverRating?: number; /** Driver uids that declined this ride while it was still "requested". * They keep the ride out of that driver's own `listNearbyRideRequests` * results without touching `status` — a decline is not a cancellation, * the ride stays open for every other nearby driver. Cleared by * `retryRide` so a passenger re-broadcast reaches everyone again. */ declinedDriverIds?: string[]; /** How many times the passenger has re-broadcast this ride after "no * driver found" (see `RideRepository.retryRide`). Capped at * `MAX_RIDE_RETRIES` — once reached, the client stops offering "try * again" and shows a final "no drivers available" state instead. */ retryCount?: number; } export type NewRideEntityData = Omit; export interface RideEntity extends RideEntityData {} export class RideEntity { constructor({ id, passengerId, driverId, status, pickupLat, pickupLng, pickupAddress, pickupGeohash, dropoffLat, dropoffLng, dropoffAddress, estimatedDistanceM, estimatedDurationS, estimatedPrice, currency, requestedAt, acceptedAt, startedAt, completedAt, canceledAt, cancelReason, passengerRating, driverRating, declinedDriverIds, retryCount, }: RideEntityData) { this.id = id; this.passengerId = passengerId; this.driverId = driverId; this.status = status; this.pickupLat = pickupLat; this.pickupLng = pickupLng; this.pickupAddress = pickupAddress; this.pickupGeohash = pickupGeohash; this.dropoffLat = dropoffLat; this.dropoffLng = dropoffLng; this.dropoffAddress = dropoffAddress; this.estimatedDistanceM = estimatedDistanceM; this.estimatedDurationS = estimatedDurationS; this.estimatedPrice = estimatedPrice; this.currency = currency; this.requestedAt = requestedAt; this.acceptedAt = acceptedAt; this.startedAt = startedAt; this.completedAt = completedAt; this.canceledAt = canceledAt; this.cancelReason = cancelReason; this.passengerRating = passengerRating; this.driverRating = driverRating; this.declinedDriverIds = declinedDriverIds; this.retryCount = retryCount; } static fromDocument(doc: QueryDocumentSnapshot): RideEntity { const data = doc.data() as RideEntityData; data.id = doc.id; return new RideEntity(data); } } export class RideAdapter { static toMap(data: Partial): { [key: string]: unknown } { return cleanEntityId(data); } }