import { calculateFare } from "@/lib/pricing"; import { DEFAULT_SERVICE, type ServiceId } from "@/constants/services"; import type { Driver, MarkerData } from "@/types/type"; const directionsAPI = process.env.EXPO_PUBLIC_GOOGLE_API_KEY; // Build map markers from driver rows. Only drivers reporting a real GPS // position are shown — there is no fallback/scatter, so drivers without a // position (none, now that seed fixtures are gone) simply aren't rendered. export const generateMarkersFromData = ({ data, }: { data: Driver[]; userLatitude: number; userLongitude: number; }): MarkerData[] => { return data .filter((driver) => driver.latitude != null && driver.longitude != null) .map((driver) => ({ ...driver, latitude: driver.latitude as number, longitude: driver.longitude as number, title: `${driver.first_name} ${driver.last_name}`, })); }; export const calculateRegion = ({ userLatitude, userLongitude, destinationLatitude, destinationLongitude, }: { userLatitude: number | null; userLongitude: number | null; destinationLatitude?: number | null; destinationLongitude?: number | null; }) => { if (!userLatitude || !userLongitude) { // Default to Beirut, Lebanon. return { latitude: 33.8938, longitude: 35.5018, latitudeDelta: 0.09, longitudeDelta: 0.09, }; } if (!destinationLatitude || !destinationLongitude) { return { latitude: userLatitude, longitude: userLongitude, latitudeDelta: 0.01, longitudeDelta: 0.01, }; } const minLat = Math.min(userLatitude, destinationLatitude); const maxLat = Math.max(userLatitude, destinationLatitude); const minLng = Math.min(userLongitude, destinationLongitude); const maxLng = Math.max(userLongitude, destinationLongitude); const latitudeDelta = (maxLat - minLat) * 1.3; // Adding some padding const longitudeDelta = (maxLng - minLng) * 1.3; // Adding some padding const latitude = (userLatitude + destinationLatitude) / 2; const longitude = (userLongitude + destinationLongitude) / 2; return { latitude, longitude, latitudeDelta, longitudeDelta, }; }; // Per-driver ETA + fare. The rider pays for the trip leg only (distance + // duration) — never the driver's approach leg. `service` drives the fare // multiplier. export const calculateDriverTimes = async ({ markers, userLatitude, userLongitude, destinationLatitude, destinationLongitude, service = DEFAULT_SERVICE, }: { markers: MarkerData[]; userLatitude: number | null; userLongitude: number | null; destinationLatitude: number | null; destinationLongitude: number | null; service?: ServiceId; }) => { if ( !userLatitude || !userLongitude || !destinationLatitude || !destinationLongitude ) return; try { const timesPromises = markers.map(async (marker) => { const responseToUser = await fetch( `https://maps.googleapis.com/maps/api/directions/json?origin=${marker.latitude},${marker.longitude}&destination=${userLatitude},${userLongitude}&key=${directionsAPI}`, ); const dataToUser = await responseToUser.json(); const responseToDestination = await fetch( `https://maps.googleapis.com/maps/api/directions/json?origin=${userLatitude},${userLongitude}&destination=${destinationLatitude},${destinationLongitude}&key=${directionsAPI}`, ); const dataToDestination = await responseToDestination.json(); // Google returns no routes when a leg is unreachable (ZERO_RESULTS). const legToUser = dataToUser.routes?.[0]?.legs?.[0]; const legToDestination = dataToDestination.routes?.[0]?.legs?.[0]; if (!legToUser || !legToDestination) { return { ...marker, time: 0, price: "0.00" }; } const timeToUser = legToUser.duration.value; // Pickup ETA in seconds const timeToDestination = legToDestination.duration.value; // Trip duration in seconds // The rider pays for the trip leg only (distance + duration) — // never for the driver's approach. const price = calculateFare( { distanceMeters: legToDestination.distance.value, durationSeconds: timeToDestination, }, service, ); const totalTripTime = (timeToUser + timeToDestination) / 60; // Minutes until drop-off return { ...marker, time: totalTripTime, price }; }); return await Promise.all(timesPromises); } catch (error) { console.error("Error calculating driver times:", error); } }; // A single trip-leg fare estimate for the confirm-ride screen. One Directions // call instead of one per driver, since the trip leg is the same regardless of // which driver arrives. Returns { fare, durationSeconds, distanceMeters } or // null when the route is unreachable. export const calculateTripFare = async ({ userLatitude, userLongitude, destinationLatitude, destinationLongitude, service = DEFAULT_SERVICE, }: { userLatitude: number | null; userLongitude: number | null; destinationLatitude: number | null; destinationLongitude: number | null; service?: ServiceId; }): Promise<{ fare: string; durationSeconds: number; distanceMeters: number; } | null> => { if ( !userLatitude || !userLongitude || !destinationLatitude || !destinationLongitude ) return null; try { const response = await fetch( `https://maps.googleapis.com/maps/api/directions/json?origin=${userLatitude},${userLongitude}&destination=${destinationLatitude},${destinationLongitude}&key=${directionsAPI}`, ); const data = await response.json(); const leg = data.routes?.[0]?.legs?.[0]; if (!leg) return null; return { fare: calculateFare( { distanceMeters: leg.distance.value, durationSeconds: leg.duration.value, }, service, ), durationSeconds: leg.duration.value, distanceMeters: leg.distance.value, }; } catch (error) { console.error("Error calculating trip fare:", error); return null; } };