// Naturreservat — Leaflet tile map with real Naturvårdsverket data.

const { useState, useRef, useEffect, useCallback } = React;

// ---------- THEME ----------
const THEME = {
  font: "'Inter', system-ui, sans-serif",
  displayWeight: 600,
  bodyWeight: 400,
  letterSpacing: "-0.01em",
  radius: 18,
  sheetRadius: 28,
  bg: "#f3efe9",
  surface: "#ffffff",
  surfaceAlt: "#f8f5f0",
  text: "#1f1d1a",
  textMuted: "#6b6760",
  border: "rgba(31,29,26,0.08)",
  borderStrong: "rgba(31,29,26,0.14)",
  accent: "#3a4a32",
  accentText: "#ffffff",
  pin: "#3a4a32",
  pinPark: "#a85a2a",
  chip: { bg: "#ffffff", border: "rgba(31,29,26,0.10)", text: "#1f1d1a" },
};

// Build SVG pin HTML for a Leaflet divIcon
function pinSvg(color, isNationalpark, scale = 1) {
  const w = Math.round(32 * scale), h = Math.round(42 * scale);
  const parkPath = isNationalpark
    ? `<path d="M -2.6 -15 L -0.5 -11.5 L 2.8 -16 L 2.8 -10.5 L -2.6 -10.5 Z" fill="${color}"/>`
    : "";
  return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="-16 -28 32 42">
    <ellipse cx="0" cy="3" rx="11" ry="3" fill="rgba(0,0,0,0.18)"/>
    <path d="M 0 -28 C -10 -28 -16 -20 -16 -12 C -16 -4 -10 4 0 14 C 10 4 16 -4 16 -12 C 16 -20 10 -28 0 -28 Z"
          fill="${color}" stroke="white" stroke-width="2.5"/>
    <circle cx="0" cy="-13" r="5.5" fill="white"/>
    ${parkPath}
  </svg>`;
}

// ---------- MAP VIEW ----------
function MapView({ reserves, selectedId, userPos, insideReserve, reservesLoaded, onPinClick, mapApiRef }) {
  const containerRef = useRef(null);
  const mapRef = useRef(null);
  const layersRef = useRef(new Map()); // id → { marker, circle }
  const userMarkerRef = useRef(null);
  const initialPanRef = useRef(false);
  const [mapReady, setMapReady] = useState(false);

  // Initialize Leaflet map once on mount
  useEffect(() => {
    if (mapRef.current) return;
    const map = L.map(containerRef.current, {
      zoomControl: false,
      attributionControl: false,
      center: [59.33, 18.06],
      zoom: 15,
    });
    L.tileLayer(
      "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png",
      { subdomains: "abcd", maxZoom: 19 }
    ).addTo(map);
    mapRef.current = map;
    if (mapApiRef) mapApiRef.current = map;
    // whenReady fires after the map container is sized and the first render is done
    map.whenReady(() => setMapReady(true));
  }, []);

  // Pan to user once on the first position fix — never again so manual zoom is preserved
  useEffect(() => {
    if (!mapReady || !userPos || initialPanRef.current) return;
    initialPanRef.current = true;
    mapRef.current.setView([userPos.lat, userPos.lon], 15, { animate: false });
  }, [mapReady, userPos?.lat, userPos?.lon]);

  // User location dot + 1 km diameter circle
  useEffect(() => {
    if (!mapReady || !userPos) return;
    userMarkerRef.current?.remove();

    const group = L.layerGroup().addTo(mapRef.current);

    // 1 km diameter proximity circle (500 m radius)
    L.circle([userPos.lat, userPos.lon], {
      radius: 500,
      color: THEME.accent,
      weight: 2,
      dashArray: "8 6",
      opacity: 0.8,
      fillColor: THEME.accent,
      fillOpacity: 0.07,
      interactive: false,
    }).addTo(group);

    // User dot
    const icon = L.divIcon({
      className: "naturreservat-pin",
      html: `<div style="width:18px;height:18px;border-radius:50%;background:white;border:3px solid ${THEME.accent};box-shadow:0 0 0 6px ${THEME.accent}22;"></div>`,
      iconSize: [18, 18],
      iconAnchor: [9, 9],
    });
    L.marker([userPos.lat, userPos.lon], { icon, zIndexOffset: 2000, interactive: false }).addTo(group);

    userMarkerRef.current = group;
  }, [mapReady, userPos?.lat, userPos?.lon]);

  // "Du är i ett naturreservat!" label — separate effect so it updates when insideReserve changes
  const insideLabelRef = useRef(null);
  useEffect(() => {
    insideLabelRef.current?.remove();
    insideLabelRef.current = null;
    if (!mapReady || !userPos || !reservesLoaded) return;
    const text = insideReserve ? "Du är i ett naturreservat!" : "Du är inte i ett naturreservat!";
    const bg = insideReserve ? THEME.accent : THEME.textMuted;
    const labelIcon = L.divIcon({
      className: "naturreservat-pin",
      html: `<div style="
        display:inline-block;
        background:${bg};color:white;
        font-family:${THEME.font};font-size:12px;font-weight:600;
        padding:5px 10px;border-radius:20px;
        white-space:nowrap;box-shadow:0 2px 8px rgba(0,0,0,0.25);
        position:relative;left:18px;top:0;transform:translateY(-50%);
      ">${text}</div>`,
      iconSize: [0, 0],
      iconAnchor: [0, 0],
    });
    insideLabelRef.current = L.marker([userPos.lat, userPos.lon], {
      icon: labelIcon, zIndexOffset: 1999, interactive: false,
    }).addTo(mapRef.current);
  }, [mapReady, userPos?.lat, userPos?.lon, insideReserve, reservesLoaded]);

  // Reserve markers & boundary circles — only run once map is confirmed ready
  useEffect(() => {
    if (!mapReady) return;
    const map = mapRef.current;

    // Remove previous layers
    layersRef.current.forEach(({ marker, circle }) => { marker.remove(); circle.remove(); });
    layersRef.current.clear();

    reserves.forEach((r) => {
      const isSelected = r.id === selectedId;
      const color = r.typ === "Nationalpark" ? THEME.pinPark : THEME.pin;
      const scale = isSelected ? 1.3 : 1;
      const w = Math.round(32 * scale), h = Math.round(42 * scale);

      const icon = L.divIcon({
        className: "naturreservat-pin",
        html: pinSvg(color, r.typ === "Nationalpark", scale),
        iconSize: [w, h],
        iconAnchor: [Math.round(w / 2), Math.round(h * 0.33)],
      });

      const marker = L.marker([r.lat, r.lon], { icon, zIndexOffset: isSelected ? 1000 : 0 })
        .addTo(map)
        .on("click", () => onPinClick(r));

      // Actual polygon boundary from WFS geometry
      const boundary = L.geoJSON(r.geojson, {
        style: {
          color,
          weight: 2,
          dashArray: "4 7",
          fillColor: color,
          fillOpacity: isSelected ? 0.12 : 0.05,
          interactive: false,
        },
      }).addTo(map);

      layersRef.current.set(r.id, { marker, circle: boundary });
    });
  }, [mapReady, reserves, selectedId]);

  return <div ref={containerRef} style={{ position: "absolute", inset: 0 }} />;
}

// ---------- BOTTOM SHEET ----------
function BottomSheet({ reserve, onClose }) {
  const [drag, setDrag] = useState(null);
  const [offset, setOffset] = useState(0);
  const [desc, setDesc] = useState(null);
  const [loadingDesc, setLoadingDesc] = useState(false);

  useEffect(() => {
    setOffset(0);
    setDesc(null);
    if (!reserve) return;
    setLoadingDesc(true);
    window.fetchReserveDetail(reserve.id).then(d => { setDesc(d); setLoadingDesc(false); });
  }, [reserve?.id]);

  if (!reserve) return null;

  const onDown = (e) => { e.currentTarget.setPointerCapture?.(e.pointerId); setDrag({ id: e.pointerId, y: e.clientY, off: offset }); };
  const onMove = (e) => { if (drag?.id === e.pointerId) setOffset(Math.max(0, e.clientY - drag.y + drag.off)); };
  const onUp = () => { if (offset > 120) onClose(); else setOffset(0); setDrag(null); };

  const t = THEME;
  return (
    <div style={{
      position: "absolute", left: "50%", transform: `translateX(-50%) translateY(${offset}px)`,
      bottom: 0, width: "100%", maxWidth: 480,
      maxHeight: "70vh", overflowY: "auto",
      background: t.surface,
      borderTopLeftRadius: t.sheetRadius, borderTopRightRadius: t.sheetRadius,
      boxShadow: "0 -8px 32px rgba(0,0,0,0.12)",
      transition: drag ? "none" : "transform 0.32s cubic-bezier(0.2,0.8,0.2,1)",
      color: t.text, fontFamily: t.font,
      padding: "10px 22px 28px",
      animation: "sheetIn 0.32s cubic-bezier(0.2,0.8,0.2,1)",
      zIndex: 800,
    }}>
      <div style={{ padding: "8px 0 14px", cursor: "grab", touchAction: "none" }}
           onPointerDown={onDown} onPointerMove={onMove} onPointerUp={onUp} onPointerCancel={onUp}>
        <div style={{ width: 40, height: 4, borderRadius: 2, background: t.borderStrong, margin: "0 auto" }} />
      </div>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 12 }}>
        <span style={{ fontSize: 11, letterSpacing: "0.16em", textTransform: "uppercase", color: reserve.typ === "Nationalpark" ? t.pinPark : t.pin, fontWeight: 600 }}>{reserve.typ}</span>
        <button onClick={onClose} style={{ width: 32, height: 32, borderRadius: 999, background: t.surfaceAlt, border: `1px solid ${t.border}`, color: t.textMuted, fontSize: 16, cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center" }}>×</button>
      </div>
      <h2 style={{ margin: 0, fontWeight: t.displayWeight, fontSize: 26, lineHeight: 1.15, letterSpacing: t.letterSpacing }}>{reserve.namn}</h2>
      <div style={{ display: "flex", gap: 18, marginTop: 14, marginBottom: 16 }}>
        {reserve.storlek > 0 && <Stat label="Storlek" value={`${reserve.storlek.toLocaleString("sv-SE")} ha`} />}
        {reserve.ar && <Stat label="Inrättat" value={reserve.ar} />}
        <Stat label="Avstånd" value={`${reserve.avstand.toString().replace(".", ",")} km`} />
      </div>
      <div style={{ height: 1, background: t.border, margin: "4px 0 16px" }} />
      {loadingDesc
        ? <p style={{ margin: 0, fontSize: 14, color: t.textMuted, fontStyle: "italic" }}>Hämtar beskrivning…</p>
        : desc
          ? <p style={{ margin: 0, fontSize: 15, lineHeight: 1.55 }}>{desc}</p>
          : <p style={{ margin: 0, fontSize: 14, color: t.textMuted, fontStyle: "italic" }}>Ingen beskrivning tillgänglig.</p>
      }
      <button style={{ marginTop: 22, width: "100%", background: t.accent, color: t.accentText, border: "none", borderRadius: t.radius, padding: "16px 18px", fontSize: 15, fontWeight: 600, cursor: "pointer" }}>
        Vägbeskrivning
      </button>
    </div>
  );
}

function Stat({ label, value }) {
  const t = THEME;
  return (
    <div>
      <div style={{ fontSize: 10, letterSpacing: "0.14em", textTransform: "uppercase", color: t.textMuted, marginBottom: 4, fontWeight: 500 }}>{label}</div>
      <div style={{ fontWeight: t.displayWeight, fontSize: 17, letterSpacing: t.letterSpacing }}>{value}</div>
    </div>
  );
}

// ---------- ZOOM + LOCATE ----------
function ZoomControls({ onZoomIn, onZoomOut, onLocate }) {
  const t = THEME;
  const btn = { width: 44, height: 44, background: t.surface, border: `1px solid ${t.border}`, color: t.text, fontSize: 20, cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center" };
  return (
    <div style={{ position: "absolute", right: 16, bottom: 40, zIndex: 800, display: "flex", flexDirection: "column", borderRadius: t.radius, overflow: "hidden", boxShadow: "0 2px 10px rgba(0,0,0,0.12)" }}>
      <button style={btn} onClick={onZoomIn}>+</button>
      <div style={{ height: 1, background: t.border }} />
      <button style={btn} onClick={onZoomOut}>−</button>
      <div style={{ height: 1, background: t.border }} />
      <button style={{ ...btn, color: t.accent }} onClick={onLocate}>
        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="3" /><path d="M12 2v3M12 19v3M2 12h3M19 12h3" /></svg>
      </button>
    </div>
  );
}

// ---------- LEGEND ----------
function Legend() {
  const t = THEME;
  return (
    <div style={{ position: "absolute", left: 16, top: 16, zIndex: 800, background: t.chip.bg, border: `1px solid ${t.chip.border}`, borderRadius: t.radius, padding: "10px 14px", fontFamily: t.font, fontSize: 12, boxShadow: "0 2px 8px rgba(0,0,0,0.08)", display: "flex", alignItems: "center", gap: 10 }}>
      <strong style={{ fontSize: 13, marginRight: 4, letterSpacing: "-0.01em" }}>Naturreservat</strong>
      <div style={{ width: 1, height: 14, background: t.border }} />
      <div style={{ display: "flex", alignItems: "center", gap: 6 }}><div style={{ width: 9, height: 9, borderRadius: "50%", background: t.pin }} /><span style={{ color: t.textMuted }}>Naturreservat</span></div>
      <div style={{ display: "flex", alignItems: "center", gap: 6 }}><div style={{ width: 9, height: 9, borderRadius: "50%", background: t.pinPark }} /><span style={{ color: t.textMuted }}>Nationalpark</span></div>
    </div>
  );
}

// ---------- OVERLAYS ----------
function LoadingOverlay({ message }) {
  const t = THEME;
  return (
    <div style={{ position: "absolute", inset: 0, zIndex: 900, background: t.bg, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 16, fontFamily: t.font, color: t.textMuted }}>
      <svg width="40" height="40" viewBox="0 0 40 40" fill="none">
        <circle cx="20" cy="20" r="16" stroke={t.border} strokeWidth="3" />
        <path d="M20 4 A16 16 0 0 1 36 20" stroke={t.accent} strokeWidth="3" strokeLinecap="round">
          <animateTransform attributeName="transform" type="rotate" from="0 20 20" to="360 20 20" dur="0.9s" repeatCount="indefinite" />
        </path>
      </svg>
      <span style={{ fontSize: 15 }}>{message}</span>
    </div>
  );
}

function ErrorOverlay({ message, onRetry }) {
  const t = THEME;
  return (
    <div style={{ position: "absolute", inset: 0, zIndex: 900, background: t.bg, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 16, fontFamily: t.font, padding: "0 32px", textAlign: "center" }}>
      <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke={t.textMuted} strokeWidth="1.5"><circle cx="12" cy="12" r="10"/><path d="M12 8v4M12 16h.01" strokeLinecap="round"/></svg>
      <p style={{ margin: 0, fontSize: 15, lineHeight: 1.5, color: t.textMuted }}>{message}</p>
      {onRetry && <button onClick={onRetry} style={{ background: t.accent, color: t.accentText, border: "none", borderRadius: t.radius, padding: "12px 24px", fontSize: 14, fontWeight: 600, cursor: "pointer" }}>Försök igen</button>}
    </div>
  );
}

// ---------- APP ----------
window.NaturreservatApp = function NaturreservatApp() {
  const mapApiRef = useRef(null);
  const [selected, setSelected] = useState(null);
  const [userPos, setUserPos] = useState(null);
  const [reserves, setReserves] = useState([]);
  const [status, setStatus] = useState("locating");
  const [insideReserve, setInsideReserve] = useState(false);
  const fetchAnchorRef = useRef(null); // lat/lon of last reserve fetch
  const reservesRef = useRef([]);      // mirror of reserves for use in callbacks

  const load = useCallback((lat, lon) => {
    fetchAnchorRef.current = { lat, lon };
    setStatus("loading");
    window.fetchReservesNearby(lat, lon, 0.27) // ~30 km radius
      .then(data => {
        reservesRef.current = data;
        setReserves(data);
        setStatus("ready");
        setInsideReserve(!!window.findReserveContaining(lat, lon, data));
      })
      .catch(() => setStatus("fetch-error"));
  }, []);

  const startWatching = useCallback(() => {
    setStatus("locating");
    if (!navigator.geolocation) { setStatus("geo-error"); return; }

    const watchId = navigator.geolocation.watchPosition(
      ({ coords }) => {
        const { latitude: lat, longitude: lon } = coords;
        setUserPos({ lat, lon });

        // Re-check inside/outside on every position update
        setInsideReserve(!!window.findReserveContaining(lat, lon, reservesRef.current));

        // Re-fetch reserves if we've moved more than ~5 km from the last fetch anchor
        const anchor = fetchAnchorRef.current;
        if (!anchor) { load(lat, lon); return; }
        const dlat = lat - anchor.lat, dlon = lon - anchor.lon;
        const distDeg = Math.sqrt(dlat * dlat + dlon * dlon);
        if (distDeg > 0.045) load(lat, lon); // ~5 km
      },
      () => setStatus("geo-error"),
      { enableHighAccuracy: true, timeout: 10000, maximumAge: 5000 }
    );

    return watchId;
  }, [load]);

  useEffect(() => {
    const id = startWatching();
    return () => { if (id != null) navigator.geolocation.clearWatch(id); };
  }, []);

  const onLocate = () => {
    if (!userPos || !mapApiRef.current) return;
    mapApiRef.current.setView([userPos.lat, userPos.lon], 15, { animate: true });
  };

  return (
    <div style={{ position: "relative", width: "100%", height: "100%", overflow: "hidden", fontFamily: THEME.font }}>
      <MapView
        reserves={status === "ready" ? reserves : []}
        selectedId={selected?.id}
        userPos={userPos}
        insideReserve={insideReserve}
        reservesLoaded={status === "ready"}
        onPinClick={setSelected}
        mapApiRef={mapApiRef}
      />

      <Legend />
      <ZoomControls
        onZoomIn={() => mapApiRef.current?.zoomIn()}
        onZoomOut={() => mapApiRef.current?.zoomOut()}
        onLocate={onLocate}
      />
      <BottomSheet reserve={selected} onClose={() => setSelected(null)} />

      {status === "locating"    && <LoadingOverlay message="Hämtar din position…" />}
      {status === "loading"     && <LoadingOverlay message="Laddar naturreservat…" />}
      {status === "geo-error"   && <ErrorOverlay message="Kunde inte hämta din position. Tillåt platsåtkomst och försök igen." onRetry={requestLocation} />}
      {status === "fetch-error" && <ErrorOverlay message="Kunde inte hämta data från Naturvårdsverket." onRetry={() => userPos && load(userPos.lat, userPos.lon)} />}

      <div style={{ position: "absolute", right: 16, bottom: 16, zIndex: 800, fontSize: 10, color: THEME.textMuted, letterSpacing: "0.06em", textTransform: "uppercase", pointerEvents: "none", background: "rgba(255,255,255,0.8)", padding: "3px 7px", borderRadius: 4 }}>
        Naturvårdsverket · © CartoDB
      </div>
    </div>
  );
};
