// -----------------------------------------------------------------------------
// Immo-Ka — Agrégateur de propriétés à vendre (province de Québec)
// Auteur : Simon-Pierre Boucher — contact@spboucher.ai
// components/MapView.tsx : carte interactive « Groupe Ka »
//   · fond de carte 100 % maison (map/immokaStyle.ts) sur tuiles vectorielles
//     libres — aucune clé, aucun Mapbox
//   · pastilles de prix étirables (façon portail immobilier), rouges quand le
//     prix demandé est sous l'estimation Vrai-Prix (« bonne affaire »)
//   · clustering natif, fiche popup, sélection, signature Groupe Ka
// -----------------------------------------------------------------------------
import { useEffect, useRef } from "react";
import { useNavigate } from "react-router-dom";
import maplibregl from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
import { API_BASE, ListingFilters, listingParams, sourceName } from "../api";
import immokaStyle from "../map/immokaStyle";
import { addPills, shortPrice } from "../map/pills";
import { icoHTML } from "./Icons";

const QUEBEC_CENTER: [number, number] = [-72.5, 46.4];

function geojsonUrl(f: ListingFilters): string {
  return `${API_BASE}/api/listings.geojson?${listingParams(f)}`;
}

/** Prépare les features : prix compact des pastilles + drapeau « bonne affaire ». */
function decorate(data: GeoJSON.FeatureCollection): GeoJSON.FeatureCollection {
  for (const f of data.features) {
    const p = f.properties as Record<string, any>;
    p.p_short = shortPrice(p.price);
    p.deal = p.price != null && p.vp != null && p.price <= p.vp * 0.95 ? 1 : 0;
  }
  return data;
}

function popupHTML(p: Record<string, unknown>): string {
  const prix = p.price != null
    ? `${Number(p.price).toLocaleString("fr-CA", { maximumFractionDigits: 0 })} $`
    : (p.price_label as string) || "Prix sur demande";
  const img = p.image
    ? `<img src="${p.image}" alt="" loading="lazy"/>`
    : `<div class="mv-noimg">${icoHTML("home", 30)}</div>`;
  const meta = [
    p.property_type,
    p.bedrooms != null ? `${p.bedrooms} ch.` : "",
    p.bathrooms != null ? `${p.bathrooms} sdb` : "",
  ].filter(Boolean).join(" · ");
  const deal = p.deal
    ? `<span class="mv-pop-deal">Sous l'estimation Vrai-Prix</span>` : "";
  return `
    <div class="mv-pop">
      ${img}
      <div class="mv-pop-body">
        <div class="mv-pop-price">${prix} ${deal}</div>
        <div class="mv-pop-title">${p.address ?? p.title ?? ""}</div>
        <div class="mv-pop-meta">${meta}</div>
        <div class="mv-pop-src">${sourceName(String(p.source ?? ""))}</div>
        <a class="mv-pop-cta" href="${API_BASE}/propriete/${encodeURIComponent(String(p.uid))}">Voir la fiche →</a>
      </div>
    </div>`;
}

/** Pastille sélectionnée : encre pleine, texte blanc. */
function applySelection(map: maplibregl.Map, uid: string | null): void {
  const sel = uid ?? " ";
  map.setLayoutProperty("point-pill", "icon-image", [
    "case",
    ["==", ["get", "uid"], sel], "pill-active",
    ["==", ["get", "deal"], 1], "pill-deal",
    "pill",
  ]);
  map.setPaintProperty("point-pill", "text-color", [
    "case",
    ["==", ["get", "uid"], sel], "#ffffff",
    ["==", ["get", "deal"], 1], "#ffffff",
    "#1a1214",
  ]);
}

export default function MapView({ filters }: { filters: ListingFilters }) {
  const div = useRef<HTMLDivElement>(null);
  const mapRef = useRef<maplibregl.Map | null>(null);
  const loadedRef = useRef(false);
  const navigate = useNavigate();

  useEffect(() => {
    if (!div.current || mapRef.current) return;
    const map = new maplibregl.Map({
      container: div.current,
      style: immokaStyle,
      center: QUEBEC_CENTER,
      zoom: 6,
      attributionControl: false,
    });
    mapRef.current = map;
    map.addControl(new maplibregl.NavigationControl({ showCompass: false }), "top-right");
    map.addControl(new maplibregl.GeolocateControl({ trackUserLocation: false }), "top-right");
    map.addControl(new maplibregl.ScaleControl({ unit: "metric" }), "bottom-left");
    map.touchZoomRotate.disableRotation();

    map.on("load", () => {
      // les points doivent s'afficher même si la génération des pastilles échoue
      try { addPills(map); } catch { /* la couche point-dot reste visible */ }
      map.addSource("props", {
        type: "geojson",
        data: { type: "FeatureCollection", features: [] },
        cluster: true,
        clusterMaxZoom: 15,
        clusterRadius: 48,
      });

      // grappes — rouge Immo-Ka, halo rosé
      map.addLayer({
        id: "clusters", type: "circle", source: "props",
        filter: ["has", "point_count"],
        paint: {
          "circle-color": "#b3202b",
          "circle-radius": ["step", ["get", "point_count"], 15, 10, 19, 50, 24, 200, 30],
          "circle-stroke-width": 4,
          "circle-stroke-color": "rgba(226,55,68,0.28)",
        },
      });
      map.addLayer({
        id: "cluster-count", type: "symbol", source: "props",
        filter: ["has", "point_count"],
        layout: {
          "text-field": ["get", "point_count_abbreviated"],
          "text-font": ["Noto Sans Bold"],
          "text-size": 13,
        },
        paint: { "text-color": "#ffffff" },
      });

      // point discret sous la pastille (visible quand les pastilles se masquent)
      map.addLayer({
        id: "point-dot", type: "circle", source: "props",
        filter: ["!", ["has", "point_count"]],
        paint: {
          "circle-color": ["case", ["==", ["get", "deal"], 1], "#e23744", "#b3202b"],
          "circle-radius": 3.5,
          "circle-stroke-width": 1.2,
          "circle-stroke-color": "#f7f1ef",
        },
      });

      // pastilles de prix
      map.addLayer({
        id: "point-pill", type: "symbol", source: "props",
        filter: ["!", ["has", "point_count"]],
        layout: {
          "icon-image": ["case", ["==", ["get", "deal"], 1], "pill-deal", "pill"],
          "icon-text-fit": "both",
          "icon-text-fit-padding": [4, 9, 8, 9],
          "icon-anchor": "bottom",
          "icon-offset": [0, -2],
          "icon-allow-overlap": false,
          "text-field": ["get", "p_short"],
          "text-font": ["Noto Sans Bold"],
          "text-size": 12.5,
          "text-anchor": "bottom",
          "text-offset": [0, -0.85],
          "text-allow-overlap": false,
          "text-optional": false,
        },
        paint: {
          "text-color": ["case", ["==", ["get", "deal"], 1], "#ffffff", "#1a1214"],
        },
      });

      map.on("click", "clusters", async (e: maplibregl.MapLayerMouseEvent) => {
        const f = map.queryRenderedFeatures(e.point, { layers: ["clusters"] })[0];
        const src = map.getSource("props") as maplibregl.GeoJSONSource;
        const zoom = await src.getClusterExpansionZoom(f.properties!.cluster_id);
        map.easeTo({ center: (f.geometry as GeoJSON.Point).coordinates as [number, number], zoom });
      });

      const openPopup = (e: maplibregl.MapLayerMouseEvent) => {
        const f = e.features?.[0];
        if (!f) return;
        const uid = String((f.properties as any).uid);
        applySelection(map, uid);
        const coords = (f.geometry as GeoJSON.Point).coordinates.slice() as [number, number];
        const pop = new maplibregl.Popup({ offset: 30, maxWidth: "290px", closeButton: true })
          .setLngLat(coords).setHTML(popupHTML(f.properties as Record<string, unknown>)).addTo(map);
        pop.on("close", () => { if (mapRef.current) applySelection(map, null); });
        pop.getElement().querySelector(".mv-pop-cta")?.addEventListener("click", (ev: Event) => {
          ev.preventDefault();
          navigate(`/propriete/${encodeURIComponent(uid)}`);
        });
      };
      map.on("click", "point-pill", openPopup);
      map.on("click", "point-dot", openPopup);
      for (const layer of ["clusters", "point-pill", "point-dot"]) {
        map.on("mouseenter", layer, () => { map.getCanvas().style.cursor = "pointer"; });
        map.on("mouseleave", layer, () => { map.getCanvas().style.cursor = ""; });
      }
      loadedRef.current = true;
    });

    return () => { map.remove(); mapRef.current = null; loadedRef.current = false; };
  }, [navigate]);

  useEffect(() => {
    const map = mapRef.current;
    if (!map) return;
    let cancelled = false;
    const apply = async () => {
      try {
        const res = await fetch(geojsonUrl(filters));
        const data = decorate((await res.json()) as GeoJSON.FeatureCollection);
        if (cancelled || !mapRef.current) return;
        (map.getSource("props") as maplibregl.GeoJSONSource | undefined)?.setData(data);
        if (data.features.length > 0) {
          const b = new maplibregl.LngLatBounds();
          for (const f of data.features)
            b.extend((f.geometry as GeoJSON.Point).coordinates as [number, number]);
          map.fitBounds(b, { padding: 56, maxZoom: 14, duration: 500 });
        }
      } catch { /* réseau : la carte garde ses données précédentes */ }
    };
    if (loadedRef.current) apply();
    else map.once("load", () => apply());
    return () => { cancelled = true; };
  }, [filters]);

  return (
    <div className="mapwrap">
      <div ref={div} className="mapview" role="application" aria-label="Carte des propriétés" />
      <div className="mv-brand" aria-hidden="true">
        <span className="mv-brand-dot" /> Cartographie <b>Groupe&nbsp;Ka</b>
      </div>
      <div className="mv-legend" aria-hidden="true">
        <span><i className="mv-lg-pill" /> Prix demandé</span>
        <span><i className="mv-lg-pill mv-lg-deal" /> Sous l'estimation Vrai-Prix</span>
      </div>
      <div className="mv-credit" aria-hidden="true">Cartographie <b>Groupe&nbsp;Ka</b></div>
    </div>
  );
}
