package domain import ( "math" "time" ) const earthRadiusKm = 6371.0 // HaversineDistance calculates the distance in kilometers between two points // using the Haversine formula. The result is approximate (±5km for security). func HaversineDistance(lat1, lng1, lat2, lng2 float64) float64 { dLat := degreesToRadians(lat2 - lat1) dLng := degreesToRadians(lng2 - lng1) lat1Rad := degreesToRadians(lat1) lat2Rad := degreesToRadians(lat2) a := math.Sin(dLat/2)*math.Sin(dLat/2) + math.Cos(lat1Rad)*math.Cos(lat2Rad)* math.Sin(dLng/2)*math.Sin(dLng/2) c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a)) distance := earthRadiusKm * c // Round to nearest km for approximate distance (security) return math.Round(distance) } func degreesToRadians(degrees float64) float64 { return degrees * math.Pi / 180 } // ProductWithDistance extends Product with distance information for search results. type ProductWithDistance struct { Product DistanceKm float64 `json:"distance_km"` TenantCity string `json:"tenant_city,omitempty"` TenantState string `json:"tenant_state,omitempty"` // TenantID is hidden for anonymous browsing, revealed only at checkout } // ProductSearchFilter captures search constraints. type ProductSearchFilter struct { Search string Category string MinPriceCents *int64 MaxPriceCents *int64 ExpiresAfter *time.Time ExpiresBefore *time.Time MaxDistanceKm *float64 BuyerLat float64 BuyerLng float64 Limit int Offset int } // ProductSearchPage wraps search results with pagination. type ProductSearchPage struct { Products []ProductWithDistance `json:"products"` Total int64 `json:"total"` Page int `json:"page"` PageSize int `json:"page_size"` }