context_zone); $destination = Str::lower((string) $ride->destination_label); $hour = optional($ride->scheduled_for)->hour ?? now()->hour; return Offer::query() ->where('status', 'published') ->get() ->map(function (Offer $offer) use ($zone, $destination, $hour) { $score = (int) $offer->priority_score; $reasons = []; $haystack = Str::lower(trim(($offer->location_label ?? '').' '.$offer->title.' '.$offer->excerpt.' '.$offer->description)); if ($offer->available_now) { $score += 18; $reasons[] = 'Disponible ahora'; } if ($offer->is_featured) { $score += 14; $reasons[] = 'Partner destacado'; } foreach (array_filter([$zone, $destination]) as $needle) { if ($needle !== '' && Str::contains($haystack, $needle)) { $score += 22; $reasons[] = 'Encaja con el destino'; break; } } if ($hour >= 18 && $offer->category === 'restaurant') { $score += 12; $reasons[] = 'Muy buena opción para esta tarde/noche'; } if ($hour >= 10 && $hour <= 18 && in_array($offer->category, ['experience', 'activity'], true)) { $score += 12; $reasons[] = 'Funciona bien durante la espera o al llegar'; } if (($offer->duration_minutes ?? 0) > 0 && $offer->duration_minutes <= 120) { $score += 8; $reasons[] = 'Fácil de encajar hoy'; } if (($offer->price_from ?? 0) > 0 && $offer->price_from <= 50) { $score += 6; $reasons[] = 'Precio fácil de aceptar'; } $offer->demo_score = $score; $offer->demo_reason = collect($reasons)->unique()->take(2)->implode(' · ') ?: 'Buen encaje local'; return $offer; }) ->sortByDesc(fn (Offer $offer) => [$offer->demo_score, $offer->is_featured, $offer->priority_score]) ->take($limit) ->values(); } }