// Recherche unifiée : base canonique (annonces agrégées) + publications directes Toveli, un seul contrat de résultat. import { searchListings as searchAggregated, mapPoints as aggPoints } from "./queries"; import { searchDirect } from "./listings/public"; import type { SearchParams, SearchResult, ListingCard } from "./types"; function sortCards(items: ListingCard[], sort: SearchParams["sort"]) { const k = sort ?? "recent"; return items.sort((a, b) => k === "price_asc" ? (a.price ?? 0) - (b.price ?? 0) : k === "price_desc" ? (b.price ?? 0) - (a.price ?? 0) : k === "area_desc" ? (b.areaSqft ?? 0) - (a.areaSqft ?? 0) : k === "deal" ? ((b.ratio ?? -1) - (a.ratio ?? -1)) : (Date.parse(b.firstSeen ?? "") || 0) - (Date.parse(a.firstSeen ?? "") || 0)); } export async function unifiedSearch(p: SearchParams): Promise { const limit = Math.min(Math.max(p.limit ?? 24, 1), 500), offset = Math.max(p.offset ?? 0, 0); const direct = await searchDirect(p).catch(() => [] as ListingCard[]); if (!direct.length) return searchAggregated(p); // fusion : on récupère la fenêtre agrégée élargie puis on intercale les annonces directes selon le tri const agg = searchAggregated({ ...p, limit: Math.min(limit + direct.length, 500), offset: Math.max(0, offset - direct.length) }); const merged = sortCards([...direct, ...agg.items], p.sort); const seen = new Set(); const items = merged.filter((c) => (seen.has(c.uid) ? false : (seen.add(c.uid), true))); const start = offset < direct.length ? offset : 0; return { items: items.slice(start, start + limit), total: agg.total + direct.length, limit, offset }; } export async function unifiedPoints(p: SearchParams) { const direct = await searchDirect({ ...p, limit: 3000 }).catch(() => [] as ListingCard[]); return [...direct.filter((i) => i.lat != null && i.lng != null && i.price != null).map((i) => ({ uid: i.uid, lat: i.lat!, lng: i.lng!, price: i.price!, category: i.category, propertyId: i.propertyId })), ...aggPoints(p)]; }