﻿const { useEffect: useER, useMemo: useMR, useRef: useRR, useState: useSR } = React;

const AREA_COORDS = {
  Wongamat: { lat: 12.9695, lng: 100.8874, x: 34, y: 20 },
  Pratumnak: { lat: 12.9193, lng: 100.8618, x: 43, y: 56 },
  Jomtien: { lat: 12.8841, lng: 100.8823, x: 46, y: 80 },
  "Central Pattaya": { lat: 12.9357, lng: 100.8842, x: 43, y: 38 },
  "East Pattaya": { lat: 12.9347, lng: 100.9465, x: 72, y: 46 },
};

function defaultFiltersFromUrl() {
  const params = new URLSearchParams(window.location.search);
  return {
    type: params.get("type") || "Any",
    area: params.get("area") || "Any area",
    budget: params.get("budget") || "Any budget",
    beds: params.get("beds") || "Any",
  };
}

function writeFiltersToUrl(filters) {
  const params = new URLSearchParams();
  Object.entries(filters).forEach(([key, value]) => {
    if (value) params.set(key, value);
  });
  const next = `${window.location.pathname}?${params.toString()}`;
  window.history.replaceState(null, "", next);
}

function queryFromGoogleMapUrl(mapUrl, fallback) {
  if (!mapUrl) return fallback;
  try {
    const url = new URL(mapUrl);
    return url.searchParams.get("query") || url.searchParams.get("q") || fallback;
  } catch (_) {
    return fallback;
  }
}

function cachedMapPoint(property) {
  try {
    const cached = localStorage.getItem(`ccpMapPoint:${property.id}`);
    if (!cached) return null;
    const point = JSON.parse(cached);
    if (Number.isFinite(Number(point.lat)) && Number.isFinite(Number(point.lng))) {
      return { lat: Number(point.lat), lng: Number(point.lng) };
    }
  } catch (_) {}
  return null;
}

function listingPoint(property, index, resolvedPoint) {
  const detail = window.PROPERTY_DETAILS[property.id] || {};
  const googleMap = detail.googleMap || {};
  const area = AREA_COORDS[property.area] || AREA_COORDS["Central Pattaya"];
  const hasGoogle = googleMap.lat !== "" && googleMap.lng !== "" && Number.isFinite(Number(googleMap.lat)) && Number.isFinite(Number(googleMap.lng));
  const cached = resolvedPoint || cachedMapPoint(property);
  const jitter = (index % 5) * 0.0025;

  return {
    lat: hasGoogle ? Number(googleMap.lat) : cached ? cached.lat : area.lat + jitter,
    lng: hasGoogle ? Number(googleMap.lng) : cached ? cached.lng : area.lng + jitter,
    x: area.x + (index % 3) * 3,
    y: area.y + (index % 4) * 3,
  };
}

function filterListings(listings, filters) {
  return listings.filter((p) => {
    if (filters.type !== "Any" && p.type !== filters.type) return false;
    if (filters.area !== "Any area" && p.area !== filters.area) return false;
    if (filters.beds !== "Any") {
      const need = filters.beds === "4+" ? 4 : parseInt(filters.beds, 10);
      if (filters.beds === "4+" ? p.beds < 4 : p.beds !== need) return false;
    }
    if (filters.budget !== "Any budget") {
      const b = window.BUDGET_OPTIONS.find((o) => o.label === filters.budget);
      if (b && (p.price < b.min || p.price > b.max)) return false;
    }
    return true;
  });
}

function loadGoogleMapsScript(apiKey) {
  if (window.google?.maps) return Promise.resolve(window.google.maps);
  if (!apiKey) return Promise.reject(new Error("Missing Google Maps API key"));
  if (window.__rentalsGoogleMapsPromise) return window.__rentalsGoogleMapsPromise;

  window.__rentalsGoogleMapsPromise = new Promise((resolve, reject) => {
    const script = document.createElement("script");
    script.src = `https://maps.googleapis.com/maps/api/js?key=${encodeURIComponent(apiKey)}`;
    script.async = true;
    script.defer = true;
    script.onload = () => resolve(window.google.maps);
    script.onerror = reject;
    document.head.appendChild(script);
  });

  return window.__rentalsGoogleMapsPromise;
}

// Group listings by lat/lng so multiple units in the same building share one pin.
// Precision ~11m (4 decimals). Each group has the listings sharing that coord.
function clusterListings(listings, resolvedPoints = {}) {
  const groups = new Map();
  listings.forEach((property, index) => {
    const point = listingPoint(property, index, resolvedPoints[property.id]);
    const key = `${point.lat.toFixed(4)},${point.lng.toFixed(4)}`;
    if (!groups.has(key)) groups.set(key, { key, lat: point.lat, lng: point.lng, items: [] });
    groups.get(key).items.push(property);
  });
  return Array.from(groups.values());
}

// Animate a marker's position from `from` to `to` over `duration`ms (ease-out cubic).
function animateMarker(marker, from, to, duration) {
  const start = performance.now();
  function step(now) {
    if (!marker.getMap()) return;
    const t = Math.min(1, (now - start) / duration);
    const eased = 1 - Math.pow(1 - t, 3);
    marker.setPosition({
      lat: from.lat + (to.lat - from.lat) * eased,
      lng: from.lng + (to.lng - from.lng) * eased,
    });
    if (t < 1) requestAnimationFrame(step);
  }
  requestAnimationFrame(step);
}

// Markers for expanded cluster spread radially around the cluster center.
function spreadPositions(center, count) {
  // Radius scales gently with count so 2-item clusters don't spread too far.
  const radius = 0.0006 + Math.min(count, 8) * 0.00009;
  return Array.from({ length: count }, (_, i) => {
    const angle = (i / count) * 2 * Math.PI - Math.PI / 2;
    return {
      lat: center.lat + Math.cos(angle) * radius * 0.75,
      lng: center.lng + Math.sin(angle) * radius,
    };
  });
}

// Default icon styling for listing markers — kept stable so it doesn't redraw on hover.
function listingIcon(isHovered) {
  return {
    path: google.maps.SymbolPath.CIRCLE,
    scale: isHovered ? 10 : 8,
    fillColor: isHovered ? "#0e3e3c" : "#b8884a",
    fillOpacity: 1,
    strokeColor: "#ffffff",
    strokeWeight: 2,
  };
}

function ResultsMap({ listings, hoveredId, setHoveredId }) {
  const mapRef = useRR(null);
  const listingMarkersRef = useRR(new Map()); // propertyId -> marker (listing pins)
  const otherMarkersRef = useRR([]);          // cluster + hub markers
  const infoRef = useRR(null);
  const fittedListingsRef = useRR("");
  const [mapReady, setMapReady] = useSR(false);
  const [mapFailed, setMapFailed] = useSR(false);
  const [expandedKey, setExpandedKey] = useSR(null);
  const [geocodedPoints, setGeocodedPoints] = useSR({});
  const setHoveredRef = useRR(setHoveredId);
  setHoveredRef.current = setHoveredId;

  useER(() => {
    if (!window.GOOGLE_MAPS_API_KEY || !mapRef.current) {
      setMapFailed(true);
      return;
    }

    let cancelled = false;
    loadGoogleMapsScript(window.GOOGLE_MAPS_API_KEY)
      .then((maps) => {
        if (cancelled || !mapRef.current) return;
        const map = new maps.Map(mapRef.current, {
          center: { lat: 12.93, lng: 100.89 },
          zoom: 12,
          disableDefaultUI: true,
          zoomControl: true,
          gestureHandling: "cooperative",
          styles: [
            { featureType: "poi", stylers: [{ visibility: "off" }] },
            { featureType: "transit", stylers: [{ visibility: "off" }] },
          ],
        });
        mapRef.current.__map = map;
        // Click empty map to collapse any expanded cluster.
        map.addListener("click", () => setExpandedKey(null));
        setMapReady(true);
      })
      .catch(() => setMapFailed(true));

    return () => { cancelled = true; };
  }, []);

  useER(() => {
    if (!mapReady || !window.google?.maps) return;
    const geocoder = new google.maps.Geocoder();
    let cancelled = false;

    listings.forEach((property) => {
      if (geocodedPoints[property.id] || cachedMapPoint(property)) return;
      const googleMap = window.PROPERTY_DETAILS[property.id]?.googleMap || {};
      const hasCoords = googleMap.lat !== "" && googleMap.lng !== "" && Number.isFinite(Number(googleMap.lat)) && Number.isFinite(Number(googleMap.lng));
      if (hasCoords || !googleMap.mapUrl) return;

      const address = queryFromGoogleMapUrl(googleMap.mapUrl, `${property.title} ${property.area} Pattaya`);
      geocoder.geocode({ address, region: "TH" }, (results, status) => {
        if (cancelled || status !== "OK" || !results?.[0]?.geometry?.location) return;
        const location = results[0].geometry.location;
        const point = { lat: location.lat(), lng: location.lng() };
        try {
          localStorage.setItem(`ccpMapPoint:${property.id}`, JSON.stringify(point));
        } catch (_) {}
        setGeocodedPoints((current) => ({ ...current, [property.id]: point }));
      });
    });

    return () => { cancelled = true; };
  }, [listings, mapReady, geocodedPoints]);

  // Cluster listings whenever the result set or resolved coordinates change.
  const clusters = useMR(() => clusterListings(listings, geocodedPoints), [listings, geocodedPoints]);

  // Build / rebuild markers when clusters or expanded-cluster change.
  // hoveredId is intentionally NOT in deps — hover styling lives in a separate effect.
  useER(() => {
    const map = mapRef.current?.__map;
    if (!mapReady || !map || !window.google?.maps) return;

    // Tear down old markers.
    listingMarkersRef.current.forEach((m) => m.setMap(null));
    listingMarkersRef.current = new Map();
    otherMarkersRef.current.forEach((m) => m.setMap(null));
    otherMarkersRef.current = [];

    if (!infoRef.current) infoRef.current = new google.maps.InfoWindow({ disableAutoPan: true });

    const allBounds = new google.maps.LatLngBounds();
    const expandedBounds = new google.maps.LatLngBounds();
    const listingsKey = clusters
      .map((cluster) => `${cluster.key}:${cluster.items.map((property) => property.id).join(",")}`)
      .join("|");
    const shouldFitBounds = fittedListingsRef.current !== listingsKey;

    const renderListingMarker = (property, position, startFrom) => {
      const marker = new google.maps.Marker({
        position: startFrom || position,
        map,
        title: property.title,
        icon: listingIcon(false),
        zIndex: 100,
      });
      marker.addListener("mouseover", () => {
        setHoveredRef.current(property.id);
        infoRef.current.setContent(mapPreviewMarkup(property));
        infoRef.current.open({ anchor: marker, map });
      });
      marker.addListener("mouseout", () => {
        setHoveredRef.current(null);
        infoRef.current.close();
      });
      marker.addListener("click", (e) => {
        if (e?.domEvent) e.domEvent.stopPropagation?.();
        if (window.trackPropertyEvent) window.trackPropertyEvent(property.id, "click");
        window.location.href = window.propertyHref(property);
      });
      if (startFrom) animateMarker(marker, startFrom, position, 420);
      listingMarkersRef.current.set(property.id, marker);
      return marker;
    };

    clusters.forEach((cluster) => {
      const center = { lat: cluster.lat, lng: cluster.lng };

      if (cluster.items.length === 1) {
        const m = renderListingMarker(cluster.items[0], center);
        allBounds.extend(m.getPosition());
        return;
      }

      if (expandedKey === cluster.key) {
        // Expanded — render each unit at a radial offset, animating outward from center.
        const positions = spreadPositions(center, cluster.items.length);
        cluster.items.forEach((property, i) => {
          renderListingMarker(property, positions[i], center);
        });
        const hub = new google.maps.Marker({
          position: center,
          map,
          icon: {
            path: google.maps.SymbolPath.CIRCLE,
            scale: 6,
            fillColor: "#0e3e3c",
            fillOpacity: 0.95,
            strokeColor: "#ffffff",
            strokeWeight: 2,
          },
          title: "Collapse",
          zIndex: 50,
        });
        hub.addListener("click", (e) => {
          if (e?.domEvent) e.domEvent.stopPropagation?.();
          setExpandedKey(null);
        });
        otherMarkersRef.current.push(hub);
        positions.forEach((p) => {
          const ll = new google.maps.LatLng(p.lat, p.lng);
          allBounds.extend(ll);
          expandedBounds.extend(ll);
        });
        return;
      }

      // Collapsed cluster — single pin with count badge.
      const clusterMarker = new google.maps.Marker({
        position: center,
        map,
        title: `${cluster.items.length} units in this building`,
        label: {
          text: String(cluster.items.length),
          color: "#ffffff",
          fontSize: "12px",
          fontWeight: "700",
          fontFamily: "Geist, sans-serif",
        },
        icon: {
          path: google.maps.SymbolPath.CIRCLE,
          scale: 14,
          fillColor: "#0e3e3c",
          fillOpacity: 1,
          strokeColor: "#b8884a",
          strokeWeight: 2.5,
        },
        zIndex: 200,
      });
      clusterMarker.addListener("click", (e) => {
        if (e?.domEvent) e.domEvent.stopPropagation?.();
        if (window.trackPropertyEvent) window.trackPropertyEvent("cluster", "click");
        setExpandedKey(cluster.key);
      });
      clusterMarker.addListener("mouseover", () => {
        infoRef.current.setContent(`<div class="map-preview map-preview--google"><strong>${cluster.items.length} units</strong> at this building — click to view</div>`);
        infoRef.current.open({ anchor: clusterMarker, map });
      });
      clusterMarker.addListener("mouseout", () => infoRef.current.close());
      otherMarkersRef.current.push(clusterMarker);
      allBounds.extend(clusterMarker.getPosition());
    });

    // Zoom to the expanded cluster so the spread is visible (overrides the
    // wider "fit all listings" behaviour).
    if (expandedKey && !expandedBounds.isEmpty()) {
      map.fitBounds(expandedBounds, 120);
      // Don't allow fitBounds to over-zoom for very tight spreads.
      const listener = google.maps.event.addListenerOnce(map, "idle", () => {
        if (map.getZoom() > 17) map.setZoom(17);
      });
      // Cleanup if effect re-runs before idle fires.
      otherMarkersRef.current.__zoomListener = listener;
    } else if (shouldFitBounds && listings.length > 1) {
      map.fitBounds(allBounds, 64);
      fittedListingsRef.current = listingsKey;
    } else if (shouldFitBounds && listings.length === 1) {
      map.setCenter(allBounds.getCenter());
      map.setZoom(15);
      fittedListingsRef.current = listingsKey;
    } else if (shouldFitBounds && listings.length === 0) {
      fittedListingsRef.current = listingsKey;
    }
  }, [clusters, mapReady, expandedKey, listings]);

  // Update hover styling AND show the property preview card on the map
  // when a result card is hovered — without recreating markers.
  useER(() => {
    if (!mapReady || !window.google?.maps) return;
    const map = mapRef.current?.__map;
    listingMarkersRef.current.forEach((marker, id) => {
      const isHovered = id === hoveredId;
      marker.setIcon(listingIcon(isHovered));
      marker.setZIndex(isHovered ? 1000 : 100);
    });

    if (!infoRef.current) infoRef.current = new google.maps.InfoWindow({ disableAutoPan: true });

    if (!hoveredId) {
      infoRef.current.close();
      return;
    }

    const property = listings.find((p) => p.id === hoveredId);
    if (!property || !map) return;
    infoRef.current.setContent(mapPreviewMarkup(property));

    const marker = listingMarkersRef.current.get(hoveredId);
    if (marker) {
      // Anchor to the individual marker.
      infoRef.current.open({ anchor: marker, map });
    } else {
      // Property is inside a collapsed cluster — open at its coordinates.
      const idx = listings.findIndex((p) => p.id === hoveredId);
      const point = listingPoint(property, idx, geocodedPoints[property.id]);
      infoRef.current.setPosition({ lat: point.lat, lng: point.lng });
      infoRef.current.open({ map });
    }
  }, [hoveredId, mapReady, expandedKey, listings, geocodedPoints]);

  // Collapse any expanded cluster when the underlying result set changes.
  useER(() => { setExpandedKey(null); }, [listings]);

  if (mapFailed) {
    return <FallbackResultsMap listings={listings} hoveredId={hoveredId} setHoveredId={setHoveredId}/>;
  }

  return (
    <div className="rentals-map__canvas" ref={mapRef}/>
  );
}

function FallbackResultsMap({ listings, hoveredId, setHoveredId }) {
  return (
    <div className="rentals-map__fallback">
      <div className="rentals-map__sea"/>
      {listings.map((property, index) => {
        const point = listingPoint(property, index);
        return (
          <button
            key={property.id}
            className={`rentals-pin ${hoveredId === property.id ? "is-active" : ""}`}
            style={{ left: `${point.x}%`, top: `${point.y}%` }}
            onMouseEnter={() => setHoveredId(property.id)}
            onMouseLeave={() => setHoveredId(null)}
            onFocus={() => setHoveredId(property.id)}
            onBlur={() => setHoveredId(null)}
            onClick={() => { if (window.trackPropertyEvent) window.trackPropertyEvent(property.id, "click"); window.location.href = window.propertyHref(property); }}
            aria-label={property.title}
          >
            <span className="rentals-pin__dot"/>
            {hoveredId === property.id && <MapPreview property={property}/>}
          </button>
        );
      })}
    </div>
  );
}

function escapeMapPreview(value) {
  return String(value || "").replace(/[&<>"']/g, (char) => ({
    "&": "&amp;",
    "<": "&lt;",
    ">": "&gt;",
    "\"": "&quot;",
    "'": "&#039;",
  }[char]));
}

function propertySpecs(property) {
  return `${property.beds}BD / ${property.baths}BA / ${property.sqm} SQM`;
}

function previewImages(property) {
  const gallery = window.PROPERTY_DETAILS[property.id]?.gallery || [];
  return [property.image, ...gallery].filter(Boolean).filter((src, index, all) => all.indexOf(src) === index).slice(0, 4);
}

function previewStatus(property) {
  const detail = window.PROPERTY_DETAILS[property.id] || {};
  const available = window.formatAvailability(detail.availability, detail.available) || property.badge || "Available now";
  return /available now/i.test(available) ? "Available now" : available;
}

function cleanLandmarkText(value) {
  return String(value || "")
    .replace(/[•·]/g, " ")
    .replace(/\s+/g, " ")
    .trim();
}

function landmarkMinutes(item) {
  const match = `${item.time || ""} ${item.name || ""}`.match(/(\d+(?:\.\d+)?)/);
  return match ? Number(match[1]) : Number.POSITIVE_INFINITY;
}

function nearestLandmark(property) {
  const nearby = window.PROPERTY_DETAILS[property.id]?.nearby || [];
  return nearby
    .filter((item) => item && (item.name || item.time))
    .map((item, index) => ({ ...item, index }))
    .sort((a, b) => {
      const diff = landmarkMinutes(a) - landmarkMinutes(b);
      return diff || a.index - b.index;
    })[0];
}

function previewDistance(property) {
  const landmark = nearestLandmark(property);
  if (!landmark) return "";
  const time = cleanLandmarkText(landmark.time).replace(/\s+to$/i, "");
  const name = cleanLandmarkText(landmark.name);
  if (time && name) return `${time} to ${name}`;
  return name || time;
}

function mapPreviewMarkup(property) {
  const images = previewImages(property);
  const dots = images.map((_, index) => `<span class="pcard__dot ${index === 0 ? "is-active" : ""}"></span>`).join("");
  const photos = images.map((src, index) => `
        <img class="${index === 0 ? "is-active" : ""}" src="${escapeMapPreview(src)}" alt="${index === 0 ? escapeMapPreview(property.title) : ""}">
      `).join("");
  return `
    <a class="map-preview map-preview--google pcard" href="${escapeMapPreview(window.propertyHref(property))}">
      <span class="pcard__photo">
        ${photos}
        <span class="pcard__status">${escapeMapPreview(previewStatus(property))}</span>
        <span class="pcard__save" aria-hidden="true">
          <svg width="14" height="14" viewBox="0 0 24 24" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>
          </svg>
        </span>
        <span class="pcard__dots" aria-hidden="true">${dots}</span>
      </span>
      <span class="pcard__body">
        <span class="pcard__distance">${escapeMapPreview(previewDistance(property))}</span>
        <strong class="pcard__name">${escapeMapPreview(property.title)}</strong>
        <span class="pcard__specs">${escapeMapPreview(propertySpecs(property))}</span>
        <span class="pcard__price">
          <span class="pcard__price-amount">${escapeMapPreview(window.fmtTHB(property.price))}</span>
          <span class="pcard__price-cadence">/ month</span>
        </span>
      </span>
    </a>
  `;
}

function MapPreview({ property }) {
  const images = previewImages(property);
  return (
    <span className="map-preview pcard">
      <span className="pcard__photo">
        {images.map((src, index) => (
          <img key={src} className={index === 0 ? "is-active" : ""} src={src} alt={index === 0 ? property.title : ""}/>
        ))}
        <span className="pcard__status">{previewStatus(property)}</span>
        <span className="pcard__save" aria-hidden="true">
          <svg width="14" height="14" viewBox="0 0 24 24" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>
          </svg>
        </span>
        <span className="pcard__dots" aria-hidden="true">
          {images.map((src, index) => <span key={`${src}-dot`} className={`pcard__dot ${index === 0 ? "is-active" : ""}`}/>)}
        </span>
      </span>
      <span className="pcard__body">
        <span className="pcard__distance">{previewDistance(property)}</span>
        <strong className="pcard__name">{property.title}</strong>
        <span className="pcard__specs">{propertySpecs(property)}</span>
        <span className="pcard__price">
          <span className="pcard__price-amount">{window.fmtTHB(property.price)}</span>
          <span className="pcard__price-cadence">/ month</span>
        </span>
      </span>
    </span>
  );
}

function ResultCard({ property, active, setHoveredId }) {
  return (
    <a
      className={`result-card ${active ? "is-active" : ""}`}
      href={window.propertyHref(property)}
      onClick={() => window.trackPropertyEvent && window.trackPropertyEvent(property.id, "click")}
      onMouseEnter={() => setHoveredId(property.id)}
      onMouseLeave={() => setHoveredId(null)}
      onFocus={() => setHoveredId(property.id)}
      onBlur={() => setHoveredId(null)}
    >
      <img src={property.image} alt=""/>
      <div className="result-card__body">
        <div className="result-card__meta">{property.type} / {property.area} / {property.floor}</div>
        <h2>{property.title}</h2>
        <div className="result-card__features">
          {(property.features || []).slice(0, 3).map((feature) => <span key={feature}>{feature}</span>)}
        </div>
        <div className="result-card__specs">
          <span>{property.beds} bed</span>
          <span>{property.baths} bath</span>
          <span>{property.sqm} SQM</span>
          <span>{property.lease}</span>
        </div>
        <div className="result-card__foot">
          <strong className="result-card__price">{window.fmtTHB(property.price)}</strong>
          <span className="link-arrow link-arrow--sm">View</span>
        </div>
      </div>
    </a>
  );
}

function RentalsSearchApp() {
  const [filters, setFilters] = useSR(defaultFiltersFromUrl);
  const [hoveredId, setHoveredId] = useSR(null);
  const [, setListingsTick] = useSR(0);
  useER(() => {
    const onListingsUpdated = () => setListingsTick((t) => t + 1);
    window.addEventListener("listings-updated", onListingsUpdated);
    return () => window.removeEventListener("listings-updated", onListingsUpdated);
  }, []);

  const PAGE_SIZE = 30;
  const [page, setPage] = useSR(0); // zero-indexed

  const setFilter = (key, value) => {
    setFilters((current) => ({ ...current, [key]: value }));
  };

  useER(() => {
    writeFiltersToUrl(filters);
  }, [filters]);

  const filtered = useMR(() => filterListings(window.LISTINGS, filters), [filters]);

  // Reset to page 1 whenever the filters change.
  useER(() => { setPage(0); }, [filters]);

  const totalPages = Math.max(1, Math.ceil(filtered.length / PAGE_SIZE));
  const safePage = Math.min(page, totalPages - 1);
  const pageItems = filtered.slice(safePage * PAGE_SIZE, safePage * PAGE_SIZE + PAGE_SIZE);
  const activePreview = hoveredId && pageItems.some((item) => item.id === hoveredId) ? hoveredId : null;

  const goToPage = (p) => {
    const next = Math.max(0, Math.min(p, totalPages - 1));
    setPage(next);
    setHoveredId(null);
    const top = document.getElementById("top");
    if (top) top.scrollIntoView({ behavior: "smooth", block: "start" });
  };

  // Build a compact list of page numbers with ellipses, e.g. 1 … 3 4 [5] 6 7 … 12
  const pageNumbers = (() => {
    const cur = safePage + 1;
    const last = totalPages;
    const set = new Set([1, last, cur, cur - 1, cur + 1]);
    if (cur <= 2) set.add(3);
    if (cur >= last - 1) set.add(last - 2);
    const nums = [...set].filter((n) => n >= 1 && n <= last).sort((a, b) => a - b);
    const out = [];
    for (let i = 0; i < nums.length; i += 1) {
      if (i > 0 && nums[i] - nums[i - 1] > 1) out.push("…");
      out.push(nums[i]);
    }
    return out;
  })();

  return (
    <div id="top" className="rentals-page">
      <window.Header onEnquire={() => { window.location.href = "/#contact"; }}/>
      <main className="rentals-shell">
        <aside className="rentals-map" aria-label="Property map">
          <ResultsMap listings={pageItems} hoveredId={activePreview} setHoveredId={setHoveredId}/>
        </aside>

        <section className="rentals-results">
          <div className="results-head">
            <div>
              <div className="eyebrow"><span className="eyebrow__dot"/> Search rentals</div>
              <h1>Pattaya rentals map</h1>
            </div>
            <div className="results-count">
              {filtered.length} {filtered.length === 1 ? "property" : "properties"} found
            </div>
          </div>

          <div className="results-search">
            <window.SearchPanel filters={filters} setFilters={setFilter} onSearch={() => writeFiltersToUrl(filters)}/>
          </div>

          {filtered.length ? (
            <React.Fragment>
              <div className="results-list">
                {pageItems.map((property) => (
                  <ResultCard
                    key={property.id}
                    property={property}
                    active={hoveredId === property.id}
                    setHoveredId={setHoveredId}
                  />
                ))}
              </div>
              {totalPages > 1 && (
                <nav className="pager" aria-label="Results pages">
                  <button
                    type="button"
                    className="pager__nav"
                    onClick={() => goToPage(safePage - 1)}
                    disabled={safePage === 0}
                    aria-label="Previous page"
                  >‹ Prev</button>
                  <div className="pager__nums">
                    {pageNumbers.map((n, i) => n === "…" ? (
                      <span key={`gap-${i}`} className="pager__gap">…</span>
                    ) : (
                      <button
                        key={n}
                        type="button"
                        className={`pager__num ${n === safePage + 1 ? "is-active" : ""}`}
                        onClick={() => goToPage(n - 1)}
                        aria-current={n === safePage + 1 ? "page" : undefined}
                      >{n}</button>
                    ))}
                  </div>
                  <button
                    type="button"
                    className="pager__nav"
                    onClick={() => goToPage(safePage + 1)}
                    disabled={safePage >= totalPages - 1}
                    aria-label="Next page"
                  >Next ›</button>
                </nav>
              )}
              <div className="pager__summary">
                Showing {safePage * PAGE_SIZE + 1}–{safePage * PAGE_SIZE + pageItems.length} of {filtered.length}
              </div>
            </React.Fragment>
          ) : (
            <div className="empty-results">
              <h2>No rentals match these filters.</h2>
              <p>Try widening your budget, bedrooms, or area.</p>
            </div>
          )}
        </section>
      </main>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<RentalsSearchApp/>);
