import type { FeatureFilters } from '../types'; export interface HexagonLocation { lat: number; lon: number; resolution: number; } const PROPERTY_TYPE_MAP: Record< string, { rightmove: string; onthemarket: string; zoopla: string } > = { House: { rightmove: 'detached,semi-detached,terraced', onthemarket: 'property', zoopla: '' }, Detached: { rightmove: 'detached', onthemarket: 'detached', zoopla: 'detached' }, 'Semi-Detached': { rightmove: 'semi-detached', onthemarket: 'semi-detached', zoopla: 'semi_detached', }, 'Mid-Terrace': { rightmove: 'terraced', onthemarket: 'terraced', zoopla: 'terraced' }, 'End-Terrace': { rightmove: 'terraced', onthemarket: 'terraced', zoopla: 'terraced' }, 'Enclosed Mid-Terrace': { rightmove: 'terraced', onthemarket: 'terraced', zoopla: 'terraced' }, 'Enclosed End-Terrace': { rightmove: 'terraced', onthemarket: 'terraced', zoopla: 'terraced' }, 'Flats/Maisonettes': { rightmove: 'flat', onthemarket: 'flats', zoopla: 'flat' }, Bungalow: { rightmove: 'bungalow', onthemarket: 'bungalow', zoopla: 'bungalow' }, 'Park home': { rightmove: 'park-home', onthemarket: 'property', zoopla: '' }, }; export const H3_RADIUS_MILES: Record = { 4: 15, 5: 6, 6: 3, 7: 1, 8: 0.5, 9: 0.25, 10: 0.25, 11: 0.25, 12: 0.25, }; const RIGHTMOVE_RADII = [0.25, 0.5, 1, 3, 5, 10, 15, 20, 30, 40]; const OTM_RADII = [0.25, 0.5, 1, 3, 5, 10, 15, 20, 30, 40]; const ZOOPLA_RADII = [0.25, 0.5, 1, 3, 5, 10, 15, 20, 25, 30]; function nearestRadius(target: number, allowed: number[]): number { return allowed.reduce((best, r) => (Math.abs(r - target) < Math.abs(best - target) ? r : best)); } export function buildPropertySearchUrls( location: HexagonLocation, filters: FeatureFilters ): { rightmove: string; onthemarket: string; zoopla: string } { const { lat, lon, resolution } = location; const radiusMiles = H3_RADIUS_MILES[resolution] ?? 1; const coordStr = `${lat.toFixed(5)},${lon.toFixed(5)}`; const priceFilter = filters['Last known price']; const minPrice = Array.isArray(priceFilter) && typeof priceFilter[0] === 'number' ? priceFilter[0] : undefined; const maxPrice = Array.isArray(priceFilter) && typeof priceFilter[1] === 'number' ? priceFilter[1] : undefined; const propertyTypes = filters['Property type']; const selectedTypes = Array.isArray(propertyTypes) && typeof propertyTypes[0] === 'string' ? (propertyTypes as string[]) : []; const rmParams = new URLSearchParams(); rmParams.set('searchLocation', coordStr); rmParams.set('channel', 'BUY'); rmParams.set('radius', String(nearestRadius(radiusMiles, RIGHTMOVE_RADII))); if (minPrice !== undefined) rmParams.set('minPrice', String(Math.round(minPrice))); if (maxPrice !== undefined) rmParams.set('maxPrice', String(Math.round(maxPrice))); if (selectedTypes.length > 0) { const rmTypes = [ ...new Set( selectedTypes.flatMap((t) => { const mapped = PROPERTY_TYPE_MAP[t]?.rightmove; return mapped ? mapped.split(',') : []; }) ), ]; if (rmTypes.length > 0) rmParams.set('propertyTypes', rmTypes.join(',')); } const rightmove = `https://www.rightmove.co.uk/property-for-sale/find.html?${rmParams.toString()}`; let otmType = 'property'; if (selectedTypes.length > 0) { const otmTypes = [ ...new Set(selectedTypes.map((t) => PROPERTY_TYPE_MAP[t]?.onthemarket).filter(Boolean)), ]; if (otmTypes.length === 1 && otmTypes[0] !== 'property') otmType = otmTypes[0]!; } const otmParams = new URLSearchParams(); otmParams.set('radius', String(nearestRadius(radiusMiles, OTM_RADII))); if (minPrice !== undefined) otmParams.set('min-price', String(Math.round(minPrice))); if (maxPrice !== undefined) otmParams.set('max-price', String(Math.round(maxPrice))); otmParams.set('search-site', 'geo'); otmParams.set('geo-lat', String(lat)); otmParams.set('geo-lng', String(lon)); const onthemarket = `https://www.onthemarket.com/for-sale/${otmType}/?${otmParams.toString()}`; const zParams = new URLSearchParams(); zParams.set('q', coordStr); zParams.set('search_source', 'for-sale'); zParams.set('radius', String(nearestRadius(radiusMiles, ZOOPLA_RADII))); if (minPrice !== undefined) zParams.set('price_min', String(Math.round(minPrice))); if (maxPrice !== undefined) zParams.set('price_max', String(Math.round(maxPrice))); if (selectedTypes.length > 0) { const zTypes = [ ...new Set(selectedTypes.map((t) => PROPERTY_TYPE_MAP[t]?.zoopla).filter(Boolean)), ]; for (const zt of zTypes) { zParams.append('property_sub_type', zt!); } } zParams.set('geo_autocomplete_identifier', `geo_${lat}_${lon}`); const zoopla = `https://www.zoopla.co.uk/for-sale/property/?${zParams.toString()}`; return { rightmove, onthemarket, zoopla }; }