/* Lodestay — the map. A real one: OpenStreetMap tiles under Leaflet, with the
   places as price pins, because a drawn map of Lisbon would be a wrong map of
   Lisbon. Tiles are desaturated by kit.css so the pins carry the only contrast
   on the plane, and inverted under .dark.

   Leaflet is loaded by boot.js before any screen renders, so the container in
   the ref is always in the document by the time the map is created. Nothing in
   here decides what a pin means: the caller passes the points and owns the
   selection. */
(() => {
  const { Button } = window.OriginDesignSystem_7c998b;

  /* points: [{ id, lat, lng, label, variant: "price"|"held"|"dot", marked }] */
  function MapPane({ points, center, zoom, selectedId, onSelect, interactive = true, fit = true, className }) {
    const host = React.useRef(null);
    const map = React.useRef(null);
    const layer = React.useRef(null);
    const [ready, setReady] = React.useState(!!window.L);
    const [live, setLive] = React.useState(0);

    /* Leaflet may still be in flight on a cold cache. */
    React.useEffect(() => {
      if (ready) return undefined;
      const timer = window.setInterval(() => { if (window.L) { setReady(true); window.clearInterval(timer); } }, 60);
      return () => window.clearInterval(timer);
    }, [ready]);

    React.useEffect(() => {
      if (!ready || !host.current || map.current) return undefined;
      const m = window.L.map(host.current, {
        center: center, zoom: zoom,
        zoomControl: interactive, scrollWheelZoom: false,
        dragging: interactive, doubleClickZoom: interactive, boxZoom: false,
        keyboard: interactive, attributionControl: true,
      });
      window.L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
        attribution: "© OpenStreetMap contributors", maxZoom: 19,
      }).addTo(m);
      layer.current = window.L.layerGroup().addTo(m);
      map.current = m;
      /* Exposed for verification and debugging: the map instance is otherwise
         unreachable from outside React. */
      host.current.__map = m;
      setLive((n) => n + 1);
      return () => { m.remove(); map.current = null; layer.current = null; };
    }, [ready, center, zoom, interactive]);

    /* The pane can be display:none when the map is created — below 1024px the
       list/map switch starts on the list — which leaves Leaflet believing it is
       0×0 and drawing one tile. Watching the box is the only reliable signal:
       the observer catches a switch, and the poll catches the cases where the
       element gains its box without the observer being able to report it. */
    React.useEffect(() => {
      if (!map.current || !host.current) return undefined;
      const el = host.current;
      const m = map.current;
      let last = "";
      const sync = () => {
        if (!map.current) return;
        const key = el.offsetWidth + "x" + el.offsetHeight;
        if (!el.offsetWidth || key === last) return;
        last = key;
        m.invalidateSize({ animate: false });
        if (fit && points.length > 1) m.fitBounds(points.map((p) => [p.lat, p.lng]), { padding: [56, 56], maxZoom: 15, animate: false });
      };
      sync();
      const observer = new window.ResizeObserver(sync);
      observer.observe(el);
      const poll = window.setInterval(sync, 400);
      window.addEventListener("resize", sync);
      return () => { observer.disconnect(); window.clearInterval(poll); window.removeEventListener("resize", sync); };
    }, [live, fit, points]);

    /* Markers are rebuilt whenever the points or the selection change — the set
       is small enough that this is cheaper than diffing it. */
    React.useEffect(() => {
      if (!map.current || !layer.current) return;
      layer.current.clearLayers();
      points.forEach((point) => {
        const classes = ["ls-pin"];
        if (point.variant === "held") classes.push("ls-pin--held");
        if (point.variant === "dot") classes.push("ls-pin--dot");
        if (point.marked) classes.push("ls-pin--saved");
        if (point.id === selectedId) classes.push("ls-pin--active");
        const marker = window.L.marker([point.lat, point.lng], {
          keyboard: !!onSelect,
          title: point.title || point.label,
          icon: window.L.divIcon({
            className: "ls-pin-wrap",
            html: '<span class="' + classes.join(" ") + '">' + (point.variant === "dot" ? "" : point.label) + "</span>",
            iconSize: null,
          }),
        });
        if (onSelect) marker.on("click", () => onSelect(point.id));
        marker.addTo(layer.current);
      });
      if (fit && points.length > 1) {
        map.current.fitBounds(points.map((p) => [p.lat, p.lng]), { padding: [56, 56], maxZoom: 15, animate: false });
      }
    }, [points, selectedId, onSelect, fit]);

    /* Following the selection is the map's half of the list ↔ map link. */
    React.useEffect(() => {
      if (!map.current || !selectedId) return;
      const point = points.find((p) => p.id === selectedId);
      if (point) map.current.panTo([point.lat, point.lng], { animate: true, duration: 0.4 });
    }, [selectedId, points]);

    return <div ref={host} className={"ls-map" + (className ? " " + className : "")} role="application" aria-label={"Map of " + LODESTAY_PRODUCT.city} />;
  }

  /* The card the map shows for the selected pin: the same figures the list card
     carries, in the pane the guest is looking at. */
  function MapSelectionCard({ listing, scope, onOpen, onClose }) {
    const quote = quoteForListing(listing, scope);
    const rating = ratingOf(listing);
    return (
      <div className="ls-mapcard">
        <Photo label={listing.photos[0]} icon={iconOfKind(listing)} shape="flat" />
        <div className="ls-card__body" style={{ gap: "var(--space-1)" }}>
          <span className="ls-card__name">{listing.name}</span>
          <span className="ls-card__meta">{labelOf(NEIGHBOURHOODS, listing.neighbourhood)} · {labelOf(PLACE_KINDS, listing.kind)} · sleeps {listing.sleeps}</span>
          <span className="ls-card__meta">
            {rating === null ? "New place" : formatRating(rating) + " from " + listing.reviews.length + " reviews"} · <span className="ls-price" style={{ fontSize: "var(--text-sm)" }}>{formatPrice(quote.total)}</span> for {formatNights(quote.nights)}
          </span>
        </div>
        <Button size="sm" variant="outline" onClick={() => onOpen(listing)}>View place</Button>
        <span className="ls-mapcard__close"><Button size="icon-xs" variant="ghost" aria-label="Close" onClick={onClose}><Icon name="x" size={14} /></Button></span>
      </div>
    );
  }

  Object.assign(window, { MapPane, MapSelectionCard });
})();
