// Real interactive Dalian map — Leaflet + OpenStreetMap tiles
// Hotspots use actual lat/lng coordinates (slightly fuzzed to protect wildlife locations)

// 大连市真实坐标，每个点都基于真实地名定位（非精确坐标——已模糊化处理）
const REAL_HOTSPOTS = [
  { id: "bangchui",   name: "棒棰岛一带",      en: "Bangchuidao",   lat: 38.8782, lng: 121.6920, obs: 183, deer: ["DL-001", "DL-006"],         freshness: 2,  note: "东北海岸松林带，黄昏观察最佳。敏感坐标已打码。" },
  { id: "lianhua",    name: "莲花山步道",      en: "Lianhuashan",   lat: 38.8985, lng: 121.6680, obs: 214, deer: ["DL-002", "DL-003", "DL-005"], freshness: 1,  note: "市区内最活跃片区，梅花鹿观察密度高。" },
  { id: "xinghai",    name: "星海湾后山",      en: "Xinghai",       lat: 38.8870, lng: 121.5780, obs: 88,  deer: ["DL-003"],                   freshness: 3,  note: "滨海山坡，傍晚沿海岸线出现频率较高。" },
  { id: "heishi",     name: "黑石礁沿岸",      en: "Heishijiao",    lat: 38.8662, lng: 121.5532, obs: 54,  deer: ["DL-003"],                   freshness: 4,  note: "礁石海岸与山坡相接，偶有陆生动物饮水记录。" },
  { id: "daheishan",  name: "大黑山景区",      en: "Daheishan",     lat: 39.0782, lng: 121.7720, obs: 71,  deer: ["DL-004", "FX-001"],         freshness: 3,  note: "景区外延林区，狐狸类线索需排除犬只与养殖逸散个体。" },
  { id: "laotieshan", name: "老铁山周边",      en: "Laotieshan",    lat: 38.7320, lng: 121.1380, obs: 52,  deer: ["DL-007", "SP-001"],         freshness: 8,  note: "半岛最南端，候鸟通道与斑海豹保护海域相邻。" },
  { id: "laohutan",   name: "老虎滩后山",      en: "Laohutan",      lat: 38.8717, lng: 121.6645, obs: 42,  deer: ["DL-001"],                   freshness: 3,  note: "滨海观光区北侧缓坡，与棒棰岛片区相连。" },
  { id: "jinshitan",  name: "金石滩外围",      en: "Jinshitan",     lat: 39.0625, lng: 121.9522, obs: 19,  deer: [],                            freshness: 9,  note: "开发区北界，观察报告较少但值得追踪。" },
  { id: "binhai",     name: "滨海路东段",      en: "Binhai East",   lat: 38.8615, lng: 121.6410, obs: 36,  deer: ["DL-001"],                   freshness: 6,  note: "滨海公路傍山段，常被晨跑者偶遇。" },
  { id: "changshan",  name: "长山群岛方向",    en: "Changshan",     lat: 39.2450, lng: 122.6500, obs: 28,  deer: ["EA-001"],                   freshness: 12, note: "海岸鸟类与猛禽迁徙观察区，仅保留海域聚类。" },
  { id: "longwantan", name: "龙王塘水库",      en: "Longwangtan",   lat: 38.8210, lng: 121.5050, obs: 17,  deer: [],                            freshness: 21, note: "水库林缘，观察密度低但生态完好。" },
];

// ---------- Tile layer presets ----------
const TILE_LAYERS = {
  terrain: {
    label: "地形",
    en: "Terrain",
    url: "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png",
    attribution: "© OpenTopoMap (CC-BY-SA), © OpenStreetMap",
    maxZoom: 17,
  },
  street: {
    label: "街道",
    en: "Street",
    url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
    attribution: "© OpenStreetMap contributors",
    maxZoom: 19,
  },
  satellite: {
    label: "卫星",
    en: "Satellite",
    url: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
    attribution: "© Esri, Maxar, Earthstar Geographics",
    maxZoom: 19,
  },
  toner: {
    label: "线稿",
    en: "Line",
    url: "https://tiles.stadiamaps.com/tiles/stamen_toner_lite/{z}/{x}/{y}{r}.png",
    attribution: "© Stamen, © OpenStreetMap",
    maxZoom: 18,
  },
};

// freshness color (matches site palette — vibrant warm-green primary)
function freshColor(f) {
  if (f <= 2) return "#4d8b3f";   // fresh: vibrant leaf green
  if (f <= 7) return "#e4b332";   // honey
  if (f <= 14) return "#e8954b";  // warm orange
  return "#9a8f7c";               // stale: muted stone
}

function freshLabel(f) {
  if (f <= 2) return "≤ 2 天";
  if (f <= 7) return "3–7 天";
  if (f <= 14) return "8–14 天";
  return "> 14 天";
}

// ---------- Main component ----------
function InteractiveMap({ activeId, setActiveId, onSelectDeer }) {
  const mapRef = React.useRef(null);
  const containerRef = React.useRef(null);
  const tileLayerRef = React.useRef(null);
  const markersRef = React.useRef({});
  const heatLayerRef = React.useRef(null);
  const [layerKey, setLayerKey] = React.useState("terrain");
  const [searchQ, setSearchQ] = React.useState("");
  const [timeFilter, setTimeFilter] = React.useState("all");
  const [showHeat, setShowHeat] = React.useState(false);
  const [showLabels, setShowLabels] = React.useState(true);
  const [hover, setHover] = React.useState(null);
  const [ready, setReady] = React.useState(false);

  // Filter hotspots
  const visibleHotspots = React.useMemo(() => {
    let list = REAL_HOTSPOTS;
    if (searchQ) {
      const q = searchQ.toLowerCase();
      list = list.filter(h => h.name.includes(searchQ) || h.en.toLowerCase().includes(q));
    }
    if (timeFilter !== "all") {
      const days = { "7d": 7, "30d": 30, "90d": 90 }[timeFilter];
      list = list.filter(h => h.freshness <= days);
    }
    return list;
  }, [searchQ, timeFilter]);

  const maxObs = Math.max(...REAL_HOTSPOTS.map(h => h.obs));
  const sizeOf = (obs) => 10 + (obs / maxObs) * 22;

  // ---------- init Leaflet ----------
  React.useEffect(() => {
    if (!containerRef.current || mapRef.current) return;
    let mounted = true;

    const map = window.L.map(containerRef.current, {
      center: [38.92, 121.62],
      zoom: 11,
      minZoom: 9,
      maxZoom: 17,
      zoomControl: false,
      attributionControl: true,
      scrollWheelZoom: true,
      worldCopyJump: true,
      fadeAnimation: false,
    });
    mapRef.current = map;
    // Fix tile-fade-stuck bug when container uses aspect-ratio
    const invalidateTimer = setTimeout(() => {
      if (mounted && mapRef.current) map.invalidateSize();
    }, 120);

    // attribution position
    map.attributionControl.setPosition("bottomright");

    // initial tile layer
    const cfg = TILE_LAYERS.terrain;
    tileLayerRef.current = window.L.tileLayer(cfg.url, {
      attribution: cfg.attribution,
      maxZoom: cfg.maxZoom,
      fadeAnimation: false,
    }).addTo(map);

    // custom zoom control on left
    window.L.control.zoom({ position: "topleft" }).addTo(map);

    // scale bar
    window.L.control.scale({ position: "bottomleft", imperial: false, metric: true }).addTo(map);

    // suppress flickering on layer add
    const readyTimer = setTimeout(() => {
      if (mounted) setReady(true);
    }, 80);

    return () => {
      mounted = false;
      clearTimeout(invalidateTimer);
      clearTimeout(readyTimer);
      Object.values(markersRef.current).forEach(marker => {
        try { map.removeLayer(marker); } catch (e) {}
      });
      markersRef.current = {};
      if (heatLayerRef.current) {
        try { map.removeLayer(heatLayerRef.current); } catch (e) {}
        heatLayerRef.current = null;
      }
      try { map.remove(); } catch (e) {}
      mapRef.current = null;
      tileLayerRef.current = null;
    };
  }, []);

  // ---------- swap tile layer on layer change ----------
  React.useEffect(() => {
    if (!mapRef.current) return;
    const cfg = TILE_LAYERS[layerKey];
    if (!cfg) return;
    if (tileLayerRef.current) {
      mapRef.current.removeLayer(tileLayerRef.current);
    }
    tileLayerRef.current = window.L.tileLayer(cfg.url, {
      attribution: cfg.attribution,
      maxZoom: cfg.maxZoom,
      fadeAnimation: false,
    }).addTo(mapRef.current);
  }, [layerKey]);

  // ---------- markers ----------
  React.useEffect(() => {
    const map = mapRef.current;
    if (!map || !ready) return;

    // remove old markers no longer visible
    Object.keys(markersRef.current).forEach(id => {
      if (!visibleHotspots.find(h => h.id === id)) {
        map.removeLayer(markersRef.current[id]);
        delete markersRef.current[id];
      }
    });

    visibleHotspots.forEach(h => {
      const isActive = activeId === h.id;
      const r = sizeOf(h.obs);
      const color = freshColor(h.freshness);
      const labelHtml = showLabels ? `<div class="lmap-pin-label">${h.name}<span class="num">·${h.obs}</span></div>` : "";
      const html = `
        <div class="lmap-pin-wrap${isActive ? " active" : ""}">
          ${isActive ? `<div class="lmap-pin-pulse" style="--c:${color};width:${r * 2.4}px;height:${r * 2.4}px"></div><div class="lmap-pin-pulse pulse2" style="--c:${color};width:${r * 2.4}px;height:${r * 2.4}px"></div>` : ""}
          <div class="lmap-pin" style="--c:${color};width:${r}px;height:${r}px"></div>
          ${labelHtml}
        </div>
      `;
      const icon = window.L.divIcon({
        html, className: "lmap-pin-icon",
        iconSize: [r, r], iconAnchor: [r / 2, r / 2],
      });

      let marker = markersRef.current[h.id];
      if (marker) {
        marker.setIcon(icon);
        marker.setLatLng([h.lat, h.lng]);
      } else {
        marker = window.L.marker([h.lat, h.lng], { icon, riseOnHover: true });
        marker.on("click", () => {
          setActiveId(h.id);
          map.flyTo([h.lat, h.lng], Math.max(map.getZoom(), 13), { duration: 0.7 });
        });
        marker.on("mouseover", () => setHover(h.id));
        marker.on("mouseout", () => setHover(null));
        marker.addTo(map);
        markersRef.current[h.id] = marker;
      }
    });
  }, [visibleHotspots, activeId, showLabels, ready]);

  // ---------- heatmap (manual circle overlay since heatmap plugin not loaded) ----------
  React.useEffect(() => {
    const map = mapRef.current;
    if (!map) return;
    if (heatLayerRef.current) {
      map.removeLayer(heatLayerRef.current);
      heatLayerRef.current = null;
    }
    if (showHeat) {
      const layer = window.L.layerGroup();
      visibleHotspots.forEach(h => {
        const intensity = h.obs / maxObs;
        // 多层径向圈模拟热力
        for (let i = 4; i >= 1; i--) {
          window.L.circle([h.lat, h.lng], {
            radius: 800 * i * (0.4 + intensity * 0.8),
            color: "transparent",
            fillColor: i === 1 ? "#c4632a" : "#e09a5e",
            fillOpacity: 0.08 * (5 - i) * intensity,
            interactive: false,
          }).addTo(layer);
        }
      });
      layer.addTo(map);
      heatLayerRef.current = layer;
    }
  }, [showHeat, visibleHotspots, maxObs]);

  // ---------- recenter on hotspot select ----------
  React.useEffect(() => {
    const map = mapRef.current;
    if (!map || !activeId) return;
    const h = REAL_HOTSPOTS.find(x => x.id === activeId);
    if (!h) return;
    map.flyTo([h.lat, h.lng], Math.max(map.getZoom(), 12), { duration: 0.6 });
  }, [activeId]);

  // ---------- helpers ----------
  const fitBounds = () => {
    const map = mapRef.current; if (!map) return;
    const bounds = window.L.latLngBounds(visibleHotspots.map(h => [h.lat, h.lng]));
    if (bounds.isValid()) map.flyToBounds(bounds, { padding: [60, 60], duration: 0.7 });
  };

  return (
    <div className="lmap">
      {/* Toolbar */}
      <div className="lmap-toolbar">
        <div className="lmap-search">
          <span className="lmap-search-icon">⌕</span>
          <input
            type="text"
            placeholder="搜索地点..."
            value={searchQ}
            onChange={(e) => setSearchQ(e.target.value)}
          />
          {searchQ && <button className="lmap-clear" onClick={() => setSearchQ("")}>×</button>}
        </div>

        <div className="lmap-seg">
          {Object.entries(TILE_LAYERS).map(([k, v]) => (
            <button key={k} className={layerKey === k ? "on" : ""} onClick={() => setLayerKey(k)} title={v.en}>
              <span>{v.label}</span>
            </button>
          ))}
        </div>

        <div className="lmap-seg">
          {[
            ["all", "全部"],
            ["90d", "90d"],
            ["30d", "30d"],
            ["7d", "7d"],
          ].map(([k, lab]) => (
            <button key={k} className={timeFilter === k ? "on" : ""} onClick={() => setTimeFilter(k)}>
              <span>{lab}</span>
            </button>
          ))}
        </div>

        <div className="lmap-toggles">
          <label className={"lmap-check" + (showHeat ? " on" : "")}>
            <input type="checkbox" checked={showHeat} onChange={(e) => setShowHeat(e.target.checked)} />
            <span>热力</span>
          </label>
          <label className={"lmap-check" + (showLabels ? " on" : "")}>
            <input type="checkbox" checked={showLabels} onChange={(e) => setShowLabels(e.target.checked)} />
            <span>标签</span>
          </label>
          <button className="lmap-fit" onClick={fitBounds} title="适应所有热点">⊡</button>
        </div>
      </div>

      {/* Map container */}
      <div className="lmap-wrap">
        <div ref={containerRef} className="lmap-leaflet" />

        {/* Legend */}
        <div className="lmap-legend">
          <div className="lmap-legend-title">FRESHNESS · 最近观察</div>
          {[2, 7, 14, 30].map(f => (
            <div className="lmap-legend-row" key={f}>
              <span className="dot" style={{ background: freshColor(f) }}></span>
              <span>{freshLabel(f)}</span>
            </div>
          ))}
        </div>

        {/* Hover card */}
        {hover && (() => {
          const h = REAL_HOTSPOTS.find(x => x.id === hover);
          if (!h) return null;
          return (
            <div className="lmap-hover-card">
              <div className="mono" style={{ fontSize: 10, letterSpacing: ".14em", color: "var(--muted)", marginBottom: 4 }}>
                HOTSPOT · {h.en.toUpperCase()}
              </div>
              <div className="serif" style={{ fontSize: 18, marginBottom: 6 }}>{h.name}</div>
              <div style={{ display: "flex", gap: 14, marginBottom: 6 }}>
                <div><span style={{ fontWeight: 700, fontSize: 18 }}>{h.obs}</span><span className="mono" style={{ fontSize: 10, color: "var(--muted)", marginLeft: 3 }}>OBS</span></div>
                <div><span style={{ fontWeight: 700, fontSize: 18 }}>{h.deer.length}</span><span className="mono" style={{ fontSize: 10, color: "var(--muted)", marginLeft: 3 }}>档案</span></div>
                <div><span style={{ fontWeight: 700, fontSize: 18, color: freshColor(h.freshness) }}>{h.freshness}d</span><span className="mono" style={{ fontSize: 10, color: "var(--muted)", marginLeft: 3 }}>LAST</span></div>
              </div>
              <div style={{ fontSize: 11.5, color: "var(--ink-soft)", lineHeight: 1.5 }}>{h.note}</div>
              <div className="mono" style={{ fontSize: 9, color: "var(--muted)", letterSpacing: ".1em", marginTop: 8, textTransform: "uppercase" }}>
                Clustered · exact GPS hidden
              </div>
            </div>
          );
        })()}
      </div>

      {/* Status bar */}
      <div className="lmap-status">
        <span>真实地图 · OpenStreetMap · 坐标已模糊化保护野生动物</span>
        <span className="sp"></span>
        <span className="mono">{visibleHotspots.length} / {REAL_HOTSPOTS.length} 热点</span>
        <span className="lmap-sep">·</span>
        <span className="mono">{TILE_LAYERS[layerKey].en.toUpperCase()}</span>
      </div>
    </div>
  );
}

// expose as both new + legacy name so other.jsx can keep using window.MAP_HOTSPOTS
Object.assign(window, {
  InteractiveMap,
  MAP_HOTSPOTS: REAL_HOTSPOTS,
});
