// mapview.jsx — Leaflet wrapper for hydrant browsing + location picking
//
// Exposes two components to window:
//   MapView          — full-screen tab showing all hydrants from history
//   LocationPicker   — modal picker that returns {lat, lng}

const { useState: __useS, useEffect: __useE, useRef: __useR, useMemo: __useM } = React;

// Map colors that mirror the calculator's classification tones
const MAP_TONE = {
  aa: '#5dadec',
  a:  '#4ade80',
  b:  '#f5a524',
  c:  '#f87171',
  idle: '#7a7a84',
};

// Standard OpenStreetMap tile layer — reliable and predictable
const TILE_URL  = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png';
const TILE_ATTR = '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>';
// Build a custom DivIcon shaped like a small hydrant pin.
// Kind controls the icon style: 'flow' (filled droplet) / 'residual' (gauge dot) /
// 'static' (hollow ring) / undefined (default circle).
function buildHydrantIcon(tone, label, kind, draft) {
  const color = draft ? '#7a7a84' : (MAP_TONE[tone] || '#f5a524');
  const opacity = draft ? '0.7' : '1';
  const glyph = kind === 'flow' ?
    `<svg viewBox="0 0 14 14" width="14" height="14"><path d="M7 1.5c-1.8 2.4-3 4-3 6a3 3 0 0 0 6 0c0-2-1.2-3.6-3-6z" fill="#0a0a0c" opacity="0.75"/></svg>`
    : kind === 'static' ?
    `<svg viewBox="0 0 14 14" width="14" height="14"><circle cx="7" cy="7" r="4" fill="none" stroke="#0a0a0c" stroke-width="1.8" opacity="0.85"/><circle cx="7" cy="7" r="1.4" fill="#0a0a0c" opacity="0.85"/></svg>`
    : kind === 'residual' ?
    `<svg viewBox="0 0 14 14" width="14" height="14"><circle cx="7" cy="7" r="3" fill="#0a0a0c" opacity="0.85"/></svg>`
    :
    `<svg viewBox="0 0 14 14" width="14" height="14"><circle cx="7" cy="7" r="3" fill="#0a0a0c" opacity="0.7"/></svg>`;
  const html = `
    <div class="hyd-pin" style="--c:${color};opacity:${opacity}">
      <div class="hyd-pin-body"${draft ? ' style="border:2px dashed #fafaf7"' : ''}>${glyph}</div>
      ${label ? `<div class="hyd-pin-lbl">${label}</div>` : ''}
    </div>
  `;
  return L.divIcon({
    className: 'hyd-pin-wrap',
    html,
    iconSize: [28, 28],
    iconAnchor: [14, 28],
    popupAnchor: [0, -26],
  });
}

// In-progress test state → draft pins (rendered with a dashed outline)
function buildDraftPins(inProgress) {
  if (!inProgress) return [];
  const pins = [];
  const draftPopupFooter = '<div class="hyd-popup-meta">unsaved · Save the test to keep this pin</div>';

  if (inProgress.staticLocation) {
    pins.push({
      id: 'draft:static', kind: 'static', draft: true,
      location: inProgress.staticLocation,
      markerLabel: 'S',
      popupHtml: `
        <div class="hyd-popup-title">${escapeHtml(inProgress.staticLabel || 'Static hydrant')}</div>
        ${inProgress.staticPsi ? `<div class="hyd-popup-row"><span>Static pressure</span><b>${fmtNum(inProgress.staticPsi)} psi</b></div>` : ''}
        ${inProgress.staticElevation ? `<div class="hyd-popup-row"><span>Elevation</span><b>${fmtNum(inProgress.staticElevation)} ft</b></div>` : ''}
        ${draftPopupFooter}
      `,
    });
  }
  (inProgress.residualReadings || []).forEach((r, i) => {
    if (!r.location) return;
    pins.push({
      id: `draft:r:${i}`, kind: 'residual', draft: true,
      location: r.location,
      markerLabel: `R${i + 1}`,
      popupHtml: `
        <div class="hyd-popup-title">${escapeHtml(r.label || `Residual ${i + 1}`)}</div>
        ${r.residualPsi ? `<div class="hyd-popup-row"><span>Residual</span><b>${fmtNum(r.residualPsi)} psi</b></div>` : ''}
        ${r.elevation ? `<div class="hyd-popup-row"><span>Elevation</span><b>${fmtNum(r.elevation)} ft</b></div>` : ''}
        ${draftPopupFooter}
      `,
    });
  });
  // Multi-flow-hydrant: prefer the new array if present; otherwise legacy single field
  if (Array.isArray(inProgress.flowHydrants) && inProgress.flowHydrants.length > 0) {
    inProgress.flowHydrants.forEach((f, i) => {
      if (!f.location) return;
      pins.push({
        id: `draft:flow:${f.id || i}`, kind: 'flow', draft: true,
        location: f.location,
        markerLabel: 'F' + (inProgress.flowHydrants.length > 1 ? (i + 1) : ''),
        popupHtml: `
          <div class="hyd-popup-title">${escapeHtml(f.label || `Flow hydrant ${i + 1}`)}</div>
          ${f.elevation ? `<div class="hyd-popup-row"><span>Elevation</span><b>${fmtNum(f.elevation)} ft</b></div>` : ''}
          ${draftPopupFooter}
        `,
      });
    });
  } else if (inProgress.flowLocation) {
    pins.push({
      id: 'draft:flow', kind: 'flow', draft: true,
      location: inProgress.flowLocation,
      markerLabel: 'F',
      popupHtml: `
        <div class="hyd-popup-title">Flow hydrant</div>
        ${draftPopupFooter}
      `,
    });
  }
  return pins;
}

// Flatten a history of saved tests into a flat array of map pins. Each
// saved test can produce up to N+2 pins (1 static + N residuals + 1 flow).
// Legacy entries (pre-multi-hydrant refactor) still surface as a single pin.
function flattenHistoryToPins(history) {
  const pins = [];
  // Helper to render an optional elevation row
  const elevRow = (ft) => ft
    ? `<div class="hyd-popup-row"><span>Elevation</span><b>${fmtNum(ft)} ft</b></div>`
    : '';
  history.forEach((h) => {
    const tsLabel = new Date(h.ts).toLocaleString('en-US', { dateStyle: 'medium', timeStyle: 'short' });
    const testName = escapeHtml(h.label || 'Test');
    const limitGpm = h.available;

    // Static gauge
    if (h.staticLocation) {
      const staticTitle = h.staticLabel
        ? `${testName} · ${escapeHtml(h.staticLabel)}`
        : `${testName} · Static`;
      pins.push({
        id: `${h.id}:static`, kind: 'static',
        location: h.staticLocation,
        markerLabel: `${fmtNum(h.staticPsi)}`,
        available: null,
        popupHtml: `
          <div class="hyd-popup-title">${staticTitle}</div>
          <div class="hyd-popup-row"><span>Static pressure</span><b>${fmtNum(h.staticPsi)} psi</b></div>
          ${elevRow(h.staticElevation)}
          ${limitGpm != null ? `<div class="hyd-popup-row"><span>Available @ ${h.target} psi</span><b>${fmtNum(limitGpm)} GPM</b></div>` : ''}
          <div class="hyd-popup-meta">${tsLabel}</div>
          <button class="hyd-popup-btn" data-act="load" data-id="${h.id}">Load to calculator</button>
          <button class="hyd-popup-btn danger" data-act="delete" data-id="${h.id}">Delete test</button>
        `,
      });
    }

    // Residual readings
    if (Array.isArray(h.results) && h.results.length > 0) {
      h.results.forEach((r, i) => {
        if (!r.location) return;
        // Elevation is stored on the source residualReadings entry, not the
        // computed result — look it up by id (fallback to nothing for legacy).
        const sourceReading = (h.residualReadings || []).find((x) => x.id === r.residualId);
        const elevation = sourceReading?.elevation;
        pins.push({
          id: `${h.id}:r:${i}`, kind: 'residual',
          location: r.location,
          markerLabel: r.available != null ? `${Math.round(r.available)}` : `${fmtNum(r.residualPsi)}`,
          available: r.available,
          popupHtml: `
            <div class="hyd-popup-title">${testName} · ${escapeHtml(r.label || `Residual ${i + 1}`)}</div>
            <div class="hyd-popup-row"><span>Residual</span><b>${fmtNum(r.residualPsi)} psi</b></div>
            ${r.available != null ? `<div class="hyd-popup-row"><span>Available @ ${h.target} psi</span><b>${fmtNum(r.available)} GPM</b>${r.classCode ? ` <b>(${r.classCode})</b>` : ''}</div>` : ''}
            <div class="hyd-popup-row"><span>Static</span><b>${fmtNum(h.staticPsi)} psi</b></div>
            ${elevRow(elevation)}
            <div class="hyd-popup-meta">${tsLabel}</div>
            <button class="hyd-popup-btn" data-act="load" data-id="${h.id}">Load to calculator</button>
            <button class="hyd-popup-btn danger" data-act="delete" data-id="${h.id}">Delete test</button>
          `,
        });
      });
    }

    // Flow hydrant(s) — prefer the new multi-hydrant array
    const flowHydrants = Array.isArray(h.flowHydrants) ? h.flowHydrants : null;
    if (flowHydrants && flowHydrants.length > 0) {
      flowHydrants.forEach((f, fi) => {
        if (!f.location) return;
        const contrib = f.contribGpm != null ? f.contribGpm : null;
        const title = `${testName} · ${escapeHtml(f.label || `Flow hydrant ${fi + 1}`)}`;
        pins.push({
          id: `${h.id}:flow:${f.id || fi}`, kind: 'flow',
          location: f.location,
          markerLabel: contrib != null ? `${Math.round(contrib)}` : (h.testGpm != null ? `${Math.round(h.testGpm)}` : ''),
          available: limitGpm,
          popupHtml: `
            <div class="hyd-popup-title">${title}</div>
            ${contrib != null ? `<div class="hyd-popup-row"><span>This hydrant</span><b>${fmtNum(contrib)} GPM</b></div>` : ''}
            ${flowHydrants.length > 1 ? `<div class="hyd-popup-row"><span>Total test flow</span><b>${fmtNum(h.testGpm)} GPM</b></div>` : `<div class="hyd-popup-row"><span>Test flow</span><b>${fmtNum(h.testGpm)} GPM</b></div>`}
            ${limitGpm != null ? `<div class="hyd-popup-row"><span>Available @ ${h.target} psi</span><b>${fmtNum(limitGpm)} GPM</b></div>` : ''}
            ${elevRow(f.elevation)}
            <div class="hyd-popup-meta">${tsLabel}</div>
            <button class="hyd-popup-btn" data-act="load" data-id="${h.id}">Load to calculator</button>
            <button class="hyd-popup-btn danger" data-act="delete" data-id="${h.id}">Delete test</button>
          `,
        });
      });
    } else if (h.flowLocation) {
      // Legacy single-flow-hydrant entry
      pins.push({
        id: `${h.id}:flow`, kind: 'flow',
        location: h.flowLocation,
        markerLabel: h.testGpm != null ? `${Math.round(h.testGpm)}` : '',
        available: limitGpm,
        popupHtml: `
          <div class="hyd-popup-title">${testName} · Flow hydrant</div>
          <div class="hyd-popup-row"><span>Test flow</span><b>${fmtNum(h.testGpm)} GPM</b></div>
          ${limitGpm != null ? `<div class="hyd-popup-row"><span>Available @ ${h.target} psi</span><b>${fmtNum(limitGpm)} GPM</b></div>` : ''}
          <div class="hyd-popup-meta">${tsLabel}</div>
          <button class="hyd-popup-btn" data-act="load" data-id="${h.id}">Load to calculator</button>
          <button class="hyd-popup-btn danger" data-act="delete" data-id="${h.id}">Delete test</button>
        `,
      });
    }

    // Legacy entries (pre-refactor): single h.location field
    if (h.location && !h.staticLocation && !h.flowLocation && !Array.isArray(h.results) && !flowHydrants) {
      pins.push({
        id: `${h.id}:legacy`, kind: undefined,
        location: h.location,
        markerLabel: limitGpm != null ? `${Math.round(limitGpm)}` : '',
        available: limitGpm,
        popupHtml: `
          <div class="hyd-popup-title">${testName}</div>
          ${limitGpm != null ? `<div class="hyd-popup-row"><span>Available @ ${h.target} psi</span><b>${fmtNum(limitGpm)} GPM</b></div>` : ''}
          <div class="hyd-popup-row"><span>Static / Residual</span><b>${fmtNum(h.staticPsi)} / ${fmtNum(h.residualPsi)} psi</b></div>
          <div class="hyd-popup-meta">${tsLabel}</div>
          <button class="hyd-popup-btn" data-act="load" data-id="${h.id}">Load to calculator</button>
          <button class="hyd-popup-btn danger" data-act="delete" data-id="${h.id}">Delete test</button>
        `,
      });
    }
  });
  return pins;
}

// Inject pin styles once
if (!document.getElementById('__hyd_map_styles')) {
  const s = document.createElement('style');
  s.id = '__hyd_map_styles';
  s.textContent = `
    .hyd-pin-wrap { background: transparent; border: 0; }
    .hyd-pin { position: relative; width: 28px; height: 36px; }
    .hyd-pin-body { position: absolute; left: 50%; top: 0; transform: translateX(-50%);
      width: 22px; height: 22px; border-radius: 50% 50% 50% 0;
      background: var(--c); transform: translateX(-50%) rotate(-45deg);
      box-shadow: 0 2px 6px rgba(0,0,0,.5), 0 0 0 2px #0a0a0c;
      display: flex; align-items: center; justify-content: center; }
    .hyd-pin-body svg { transform: rotate(45deg); }
    .hyd-pin-lbl { position: absolute; top: 22px; left: 50%; transform: translate(-50%, 0);
      background: #0a0a0c; color: var(--c); font: 600 9px/1 'IBM Plex Mono', monospace;
      padding: 2px 4px; border-radius: 3px; border: 1px solid var(--c);
      white-space: nowrap; letter-spacing: 0.04em; }
    .leaflet-container { background: #0a0a0c; font-family: 'Inter', system-ui, sans-serif; }
    .leaflet-popup-content-wrapper { background: #15151a; color: #fafaf7;
      border: 1px solid #2a2a31; border-radius: 10px; box-shadow: 0 8px 24px rgba(0,0,0,.5); }
    .leaflet-popup-tip { background: #15151a; border: 1px solid #2a2a31; }
    .leaflet-popup-content { margin: 12px 14px; min-width: 180px; font-size: 12.5px; }
    .leaflet-popup-close-button { color: #7a7a84 !important; padding: 6px 8px 0 0 !important; }
    .leaflet-control-zoom a { background: #15151a !important; color: #fafaf7 !important;
      border: 1px solid #2a2a31 !important; }
    .leaflet-control-zoom a:hover { background: #1d1d22 !important; }
    .leaflet-control-attribution { background: rgba(10,10,12,0.7) !important; color: #7a7a84 !important;
      font-size: 9px !important; }
    .leaflet-control-attribution a { color: #a8a8b2 !important; }
    .hyd-popup-title { font-weight: 600; font-size: 13px; margin-bottom: 4px; color: #fafaf7; }
    .hyd-popup-row { display: flex; justify-content: space-between; font-size: 11.5px; color: #a8a8b2;
      font-family: 'IBM Plex Mono', monospace; margin-top: 2px; }
    .hyd-popup-row b { color: #fafaf7; font-weight: 600; }
    .hyd-popup-meta { font-size: 10.5px; color: #7a7a84; margin-top: 6px; font-family: 'IBM Plex Mono', monospace; }
    .hyd-popup-btn { margin-top: 8px; width: 100%; padding: 6px; background: #1d1d22;
      border: 1px solid #2a2a31; border-radius: 6px; color: #fafaf7; font: 600 11px 'Inter';
      cursor: pointer; }
    .hyd-popup-btn:hover { background: #26262e; }
    .hyd-popup-btn.danger { color: #f87171; }
  `;
  document.head.appendChild(s);
}

// ─────────────────────────────────────────────────────────────────────────────
// MapView — full hydrant map

function MapView({ history, accent, onLoadEntry, onDeleteEntry, onAddHydrant, inProgress }) {
  const elRef = __useR(null);
  const mapRef = __useR(null);
  const markersRef = __useR([]);
  const [addMode, setAddMode] = __useS(false);

  // Initial center: average of ALL pin locations, else continental US fallback
  const initialCenter = __useM(() => {
    const pins = flattenHistoryToPins(history);
    if (pins.length === 0) return { lat: 39.5, lng: -98.35, zoom: 4 };
    const avg = pins.reduce(
      (s, p) => ({ lat: s.lat + p.location.lat, lng: s.lng + p.location.lng }),
      { lat: 0, lng: 0 }
    );
    return { lat: avg.lat / pins.length, lng: avg.lng / pins.length,
             zoom: pins.length === 1 ? 16 : 13 };
  }, []); // mount only

  // Initialize map once
  __useE(() => {
    if (!elRef.current || mapRef.current) return;
    const map = L.map(elRef.current, {
      center: [initialCenter.lat, initialCenter.lng],
      zoom: initialCenter.zoom,
      zoomControl: true,
      attributionControl: true,
    });
    L.tileLayer(TILE_URL, { attribution: TILE_ATTR, maxZoom: 19 }).addTo(map);
    mapRef.current = map;

    const syncSize = () => {
      requestAnimationFrame(() => map.invalidateSize({ animate: false }));
      setTimeout(() => map.invalidateSize({ animate: false }), 80);
    };

    const ro = typeof ResizeObserver !== 'undefined'
      ? new ResizeObserver(() => syncSize())
      : null;
    if (ro) ro.observe(elRef.current);
    window.addEventListener('resize', syncSize);

    map.whenReady(() => syncSize());

    // Auto-fit to bounds if multiple pins
    const pins = flattenHistoryToPins(history);
    if (pins.length > 1) {
      const bounds = L.latLngBounds(pins.map((p) => [p.location.lat, p.location.lng]));
      map.fitBounds(bounds, { padding: [40, 40], maxZoom: 17 });
      syncSize();
    }

    return () => {
      if (ro) ro.disconnect();
      window.removeEventListener('resize', syncSize);
      window.removeEventListener('orientationchange', syncSize);
      map.remove();
      mapRef.current = null;
    };
  }, []);

  // Manage markers — flatten history entries into multiple pins per entry
  // (static gauge + N residual readings + flow hydrant)
  __useE(() => {
    const map = mapRef.current;
    if (!map) return;
    markersRef.current.forEach((m) => m.remove());
    markersRef.current = [];

    const pins = [...flattenHistoryToPins(history), ...buildDraftPins(inProgress)];
    pins.forEach((p) => {
      const tone = p.available == null ? 'idle'
        : p.available >= 1500 ? 'aa'
        : p.available >= 1000 ? 'a'
        : p.available >=  500 ? 'b' : 'c';
      const marker = L.marker([p.location.lat, p.location.lng], {
        icon: buildHydrantIcon(tone, p.markerLabel, p.kind, p.draft),
      });
      marker.bindPopup(p.popupHtml);
      marker.addTo(map);
      markersRef.current.push(marker);
    });

    const onPopupClick = (e) => {
      const btn = e.target.closest('.hyd-popup-btn');
      if (!btn) return;
      const id = btn.dataset.id;
      const act = btn.dataset.act;
      const entry = history.find((h) => h.id === id);
      if (!entry) return;
      if (act === 'load') onLoadEntry(entry);
      if (act === 'delete') {
        onDeleteEntry(id);
        map.closePopup();
      }
    };
    map.getContainer().addEventListener('click', onPopupClick);
    return () => map.getContainer().removeEventListener('click', onPopupClick);
  }, [history, inProgress, onLoadEntry, onDeleteEntry]);

  // Click-to-add mode
  __useE(() => {
    const map = mapRef.current;
    if (!map) return;
    const handler = (e) => {
      if (!addMode) return;
      onAddHydrant({ lat: e.latlng.lat, lng: e.latlng.lng });
      setAddMode(false);
    };
    map.on('click', handler);
    map.getContainer().style.cursor = addMode ? 'crosshair' : '';
    return () => {
      map.off('click', handler);
      map.getContainer().style.cursor = '';
    };
  }, [addMode, onAddHydrant]);

  // Geolocation
  const goToMyLocation = () => {
    if (!navigator.geolocation) return;
    navigator.geolocation.getCurrentPosition(
      (pos) => mapRef.current?.flyTo([pos.coords.latitude, pos.coords.longitude], 17),
      () => alert('Could not get your location'),
      { enableHighAccuracy: true, timeout: 8000 }
    );
  };

  const fitAll = () => {
    const pins = flattenHistoryToPins(history);
    if (pins.length === 0) return;
    const bounds = L.latLngBounds(pins.map((p) => [p.location.lat, p.location.lng]));
    mapRef.current?.fitBounds(bounds, { padding: [40, 40], maxZoom: 17 });
  };

  const pins = __useM(() => flattenHistoryToPins(history), [history]);
  const locatedCount = pins.length;

  return (
    <div style={mapVStyles.wrap}>
      <div ref={elRef} style={mapVStyles.map} />

      {/* Address search */}
      <SearchBox mapRef={mapRef} />

      {/* Top status bar */}
      <div style={mapVStyles.statusBar}>
        <div style={mapVStyles.statusLeft}>
          <span style={mapVStyles.statusDot} />
          <span>{locatedCount} pin{locatedCount === 1 ? '' : 's'}</span>
          <span style={mapVStyles.statusUnpin}>· {history.length} test{history.length === 1 ? '' : 's'}</span>
        </div>
        <div style={mapVStyles.classKey}>
          {[['AA', '#5dadec'], ['A', '#4ade80'], ['B', '#f5a524'], ['C', '#f87171']].map(([k, c]) => (
            <span key={k} style={mapVStyles.keyItem}>
              <i style={{ background: c, width: 8, height: 8, borderRadius: '50%' }} /> {k}
            </span>
          ))}
        </div>
      </div>

      {/* Action buttons */}
      <div style={mapVStyles.actions}>
        <button onClick={goToMyLocation} style={mapVStyles.actionBtn} title="My location">
          <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
            <circle cx="8" cy="8" r="2" fill="currentColor"/>
            <circle cx="8" cy="8" r="5" stroke="currentColor" strokeWidth="1.4"/>
            <path d="M8 1v2M8 13v2M1 8h2M13 8h2" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round"/>
          </svg>
        </button>
        <button onClick={fitAll} disabled={locatedCount === 0}
                style={{ ...mapVStyles.actionBtn, opacity: locatedCount === 0 ? 0.4 : 1 }}
                title="Fit all hydrants">
          <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
            <path d="M3 5V3h2M11 3h2v2M13 11v2h-2M5 13H3v-2" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
        </button>
      </div>

      {/* Add-pin FAB */}
      <button onClick={() => setAddMode(!addMode)}
              style={{ ...mapVStyles.fab, background: addMode ? accent : '#15151a',
                       color: addMode ? '#0a0a0c' : accent,
                       borderColor: accent }}>
        {addMode ? (
          <>
            <svg width="14" height="14" viewBox="0 0 14 14"><path d="M3 3l8 8M11 3l-8 8" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round"/></svg>
            Tap map to drop pin
          </>
        ) : (
          <>
            <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
              <path d="M7 1c-2.5 0-4.5 2-4.5 4.5C2.5 9 7 13 7 13s4.5-4 4.5-7.5C11.5 3 9.5 1 7 1z" stroke="currentColor" strokeWidth="1.4"/>
              <circle cx="7" cy="5.5" r="1.5" fill="currentColor"/>
            </svg>
            Add hydrant here
          </>
        )}
      </button>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// LocationPicker — modal map for picking a single point

// SearchBox — address geocoding via OpenStreetMap Nominatim (free, no API key)

function SearchBox({ mapRef }) {
  const [q, setQ] = __useS('');
  const [results, setResults] = __useS([]);
  const [busy, setBusy] = __useS(false);
  const [open, setOpen] = __useS(false);
  const tempMarkerRef = __useR(null);
  const wrapRef = __useR(null);

  __useE(() => {
    if (!open) return;
    const onDown = (e) => { if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', onDown);
    return () => document.removeEventListener('mousedown', onDown);
  }, [open]);

  // Auto-search as the user types (debounced 400ms)
  const debounceRef = __useR(null);
  const lastQRef = __useR('');
  __useE(() => {
    if (debounceRef.current) clearTimeout(debounceRef.current);
    const text = q.trim();
    if (text.length < 3) { setResults([]); setOpen(false); return; }
    if (text === lastQRef.current) return;
    debounceRef.current = setTimeout(async () => {
      lastQRef.current = text;
      setBusy(true);
      try {
        const url = `https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(text)}&format=json&limit=6&addressdetails=1`;
        const r = await fetch(url, { headers: { 'Accept': 'application/json' } });
        const data = await r.json();
        setResults(Array.isArray(data) ? data : []);
        setOpen(true);
      } catch (err) { console.warn('autocomplete', err); }
      setBusy(false);
    }, 400);
  }, [q]);

  const search = async (e) => {
    e?.preventDefault();
    if (!q.trim()) return;
    setBusy(true);
    try {
      const url = `https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(q.trim())}&format=json&limit=6&addressdetails=1`;
      const r = await fetch(url, { headers: { 'Accept': 'application/json' } });
      const data = await r.json();
      setResults(Array.isArray(data) ? data : []);
      setOpen(true);
    } catch (err) {
      console.warn('geocode error', err);
      setResults([]);
      setOpen(true);
    }
    setBusy(false);
  };

  const choose = (r) => {
    const lat = parseFloat(r.lat), lng = parseFloat(r.lon);
    if (!isFinite(lat) || !isFinite(lng)) return;
    setOpen(false);
    setQ(r.display_name.split(',').slice(0, 2).join(', '));
    const map = mapRef.current;
    if (!map) return;
    map.flyTo([lat, lng], 17);
    if (tempMarkerRef.current) tempMarkerRef.current.remove();
    tempMarkerRef.current = L.circleMarker([lat, lng], {
      radius: 10, color: '#f5a524', weight: 2, fillColor: '#f5a524', fillOpacity: 0.3,
    }).addTo(map);
    tempMarkerRef.current.bindPopup(`<div style="font-size:11.5px;color:#fafaf7;font-family:Inter,sans-serif">${escapeHtml(r.display_name)}</div>`).openPopup();
  };

  const clearTempMarker = () => {
    if (tempMarkerRef.current) {
      tempMarkerRef.current.remove();
      tempMarkerRef.current = null;
    }
  };
  __useE(() => () => clearTempMarker(), []);

  return (
    <div ref={wrapRef} style={searchStyles.wrap}>
      <form onSubmit={search} style={searchStyles.row}>
        <svg width="14" height="14" viewBox="0 0 16 16" fill="none" style={searchStyles.icon}>
          <circle cx="7" cy="7" r="4.5" stroke="currentColor" strokeWidth="1.5"/>
          <path d="M10.5 10.5l3 3" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/>
        </svg>
        <input type="text" value={q}
          onChange={(e) => { setQ(e.target.value); if (!e.target.value) setOpen(false); }}
          onFocus={() => results.length > 0 && setOpen(true)}
          placeholder="Search address or place…"
          style={searchStyles.input} />
        {q && (
          <button type="button" onClick={() => { setQ(''); setResults([]); setOpen(false); clearTempMarker(); }}
            style={searchStyles.clear} aria-label="Clear">
            <svg width="12" height="12" viewBox="0 0 12 12"><path d="M3 3l6 6M9 3l-6 6" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/></svg>
          </button>
        )}
        <button type="submit" disabled={busy || !q.trim()} style={{
          ...searchStyles.go,
          opacity: busy || !q.trim() ? 0.5 : 1,
        }}>{busy ? '…' : 'Go'}</button>
      </form>

      {open && (
        <div style={searchStyles.results}>
          {results.length === 0 ? (
            <div style={searchStyles.empty}>No matches found</div>
          ) : (
            results.map((r, i) => (
              <button key={i} type="button" onClick={() => choose(r)} style={searchStyles.result}>
                <div style={searchStyles.resultMain}>
                  {r.display_name.split(',').slice(0, 2).join(', ')}
                </div>
                <div style={searchStyles.resultSub}>
                  {r.display_name.split(',').slice(2).join(',').trim()}
                </div>
              </button>
            ))
          )}
        </div>
      )}
    </div>
  );
}

const searchStyles = {
  wrap: { position: 'absolute', top: 12, left: '50%', transform: 'translateX(-50%)',
          width: 'min(420px, calc(100% - 110px))', zIndex: 402 },
  row: { display: 'flex', alignItems: 'center', gap: 4, padding: '4px 4px 4px 12px',
         background: 'rgba(10,10,12,0.92)', border: '1px solid #26262e',
         borderRadius: 10, backdropFilter: 'blur(12px)', WebkitBackdropFilter: 'blur(12px)' },
  icon: { color: '#7a7a84', flexShrink: 0 },
  input: { flex: 1, minWidth: 0, background: 'transparent', border: 0, outline: 'none',
           color: '#fafaf7', fontSize: 13, padding: '6px 0', fontFamily: 'inherit' },
  clear: { width: 22, height: 22, borderRadius: 5, border: 0, background: 'transparent',
           color: '#7a7a84', cursor: 'pointer', display: 'flex', alignItems: 'center',
           justifyContent: 'center', padding: 0 },
  go: { padding: '6px 11px', background: 'var(--accent)', border: 0, borderRadius: 7,
        color: '#0a0a0c', fontSize: 12, fontWeight: 700, cursor: 'pointer' },
  results: { marginTop: 6, background: 'rgba(15,15,18,0.96)', border: '1px solid #26262e',
             borderRadius: 10, overflow: 'hidden', maxHeight: 280, overflowY: 'auto',
             backdropFilter: 'blur(12px)', WebkitBackdropFilter: 'blur(12px)',
             boxShadow: '0 8px 24px rgba(0,0,0,0.5)' },
  empty: { padding: '14px', fontSize: 12, color: '#7a7a84', textAlign: 'center' },
  result: { width: '100%', padding: '9px 14px', background: 'transparent', border: 0,
            borderBottom: '1px solid #1f1f25', color: 'inherit', cursor: 'pointer',
            textAlign: 'left', display: 'flex', flexDirection: 'column', gap: 2,
            fontFamily: 'inherit' },
  resultMain: { fontSize: 12.5, color: '#fafaf7', fontWeight: 500,
                overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' },
  resultSub: { fontSize: 11, color: '#7a7a84',
               overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' },
};

function LocationPicker({ initial, onCancel, onConfirm }) {
  const elRef = __useR(null);
  const mapRef = __useR(null);
  const markerRef = __useR(null);
  const [coord, setCoord] = __useS(initial || null);

  __useE(() => {
    if (!elRef.current || mapRef.current) return;
    const start = initial || { lat: 39.5, lng: -98.35 };
    const map = L.map(elRef.current, {
      center: [start.lat, start.lng],
      zoom: initial ? 17 : 4,
      zoomControl: true,
    });
    L.tileLayer(TILE_URL, { attribution: TILE_ATTR, maxZoom: 19 }).addTo(map);
    mapRef.current = map;

    const syncSize = () => {
      requestAnimationFrame(() => map.invalidateSize({ animate: false }));
      setTimeout(() => map.invalidateSize({ animate: false }), 80);
      setTimeout(() => map.invalidateSize({ animate: false }), 250);
      setTimeout(() => map.invalidateSize({ animate: false }), 800);
    };

    const ro = typeof ResizeObserver !== 'undefined'
      ? new ResizeObserver(() => syncSize())
      : null;
    if (ro) ro.observe(elRef.current);
    window.addEventListener('resize', syncSize);
    window.addEventListener('orientationchange', syncSize);
    map.whenReady(() => syncSize());

    if (initial) {
      const m = L.marker([initial.lat, initial.lng], {
        icon: buildHydrantIcon('a', ''),
        draggable: true,
      }).addTo(map);
      m.on('dragend', () => {
        const ll = m.getLatLng();
        setCoord({ lat: ll.lat, lng: ll.lng });
      });
      markerRef.current = m;
    }

    map.on('click', (e) => {
      const ll = e.latlng;
      setCoord({ lat: ll.lat, lng: ll.lng });
      if (markerRef.current) {
        markerRef.current.setLatLng([ll.lat, ll.lng]);
      } else {
        const m = L.marker([ll.lat, ll.lng], {
          icon: buildHydrantIcon('a', ''),
          draggable: true,
        }).addTo(map);
        m.on('dragend', () => {
          const lng = m.getLatLng();
          setCoord({ lat: lng.lat, lng: lng.lng });
        });
        markerRef.current = m;
      }
    });

    return () => {
      if (ro) ro.disconnect();
      window.removeEventListener('resize', syncSize);
      window.removeEventListener('orientationchange', syncSize);
      map.remove();
      mapRef.current = null;
    };
  }, []);

  const useMyLocation = () => {
    if (!navigator.geolocation) return;
    navigator.geolocation.getCurrentPosition(
      (pos) => {
        const ll = { lat: pos.coords.latitude, lng: pos.coords.longitude };
        setCoord(ll);
        mapRef.current?.flyTo([ll.lat, ll.lng], 17);
        if (markerRef.current) {
          markerRef.current.setLatLng([ll.lat, ll.lng]);
        } else {
          const m = L.marker([ll.lat, ll.lng], {
            icon: buildHydrantIcon('a', ''),
            draggable: true,
          }).addTo(mapRef.current);
          m.on('dragend', () => {
            const x = m.getLatLng();
            setCoord({ lat: x.lat, lng: x.lng });
          });
          markerRef.current = m;
        }
      },
      () => alert('Could not get your location'),
      { enableHighAccuracy: true, timeout: 8000 }
    );
  };

  return (
    <>
      <div style={pickerStyles.scrim} onClick={onCancel} />
      <div style={pickerStyles.modal}>
        <div style={pickerStyles.head}>
          <div>
            <div style={pickerStyles.title}>Set hydrant location</div>
            <div style={pickerStyles.sub}>Tap the map, drag the pin, or use current location</div>
          </div>
          <button onClick={onCancel} style={pickerStyles.close} aria-label="Close">
            <svg width="14" height="14" viewBox="0 0 14 14">
              <path d="M3 3l8 8M11 3l-8 8" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/>
            </svg>
          </button>
        </div>
        <div ref={elRef} style={pickerStyles.map} />
        <div style={pickerStyles.footer}>
          <button onClick={useMyLocation} style={pickerStyles.locBtn}>
            <svg width="14" height="14" viewBox="0 0 16 16" fill="none" style={{ marginRight: 6 }}>
              <circle cx="8" cy="8" r="2" fill="currentColor"/>
              <circle cx="8" cy="8" r="5" stroke="currentColor" strokeWidth="1.4"/>
              <path d="M8 1v2M8 13v2M1 8h2M13 8h2" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round"/>
            </svg>
            My location
          </button>
          <div style={pickerStyles.coord} className="mono">
            {coord ? `${coord.lat.toFixed(5)}, ${coord.lng.toFixed(5)}` : 'No location set'}
          </div>
          <button onClick={() => onConfirm(coord)} disabled={!coord}
                  style={{ ...pickerStyles.confirm, opacity: coord ? 1 : 0.4,
                           cursor: coord ? 'pointer' : 'not-allowed' }}>
            Set location
          </button>
        </div>
      </div>
    </>
  );
}

// ─────────────────────────────────────────────────────────────────────────────
// Helpers

function escapeHtml(s) {
  return String(s).replace(/[&<>"']/g, (c) => ({
    '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;',
  })[c]);
}
function fmtNum(n) {
  return Number(n).toLocaleString('en-US', { maximumFractionDigits: 0 });
}

// ─────────────────────────────────────────────────────────────────────────────
// Styles

const mapVStyles = {
  wrap: { position: 'relative', width: '100%', height: '100%', minHeight: 'calc(100vh - 80px)', overflow: 'hidden' },
  map: { position: 'absolute', inset: 0, width: '100%', height: '100%' },
  statusBar: { position: 'absolute', top: 12, left: 12, right: 12, zIndex: 400,
               display: 'flex', justifyContent: 'space-between', alignItems: 'center',
               padding: '8px 12px', background: 'rgba(10,10,12,0.82)',
               border: '1px solid #26262e', borderRadius: 10, color: '#fafaf7',
               backdropFilter: 'blur(12px)', WebkitBackdropFilter: 'blur(12px)',
               fontSize: 12, fontWeight: 500 },
  statusLeft: { display: 'flex', alignItems: 'center', gap: 8 },
  statusDot: { width: 6, height: 6, borderRadius: '50%', background: '#4ade80', boxShadow: '0 0 6px #4ade80' },
  statusUnpin: { color: '#7a7a84', fontSize: 11 },
  classKey: { display: 'flex', gap: 8, fontSize: 10.5, color: '#a8a8b2', fontFamily: "'IBM Plex Mono', monospace" },
  keyItem: { display: 'inline-flex', alignItems: 'center', gap: 4 },
  actions: { position: 'absolute', top: 64, right: 12, zIndex: 400,
             display: 'flex', flexDirection: 'column', gap: 6 },
  actionBtn: { width: 36, height: 36, borderRadius: 9, background: '#15151a',
               border: '1px solid #26262e', color: '#a8a8b2', cursor: 'pointer',
               display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 0 },
  fab: { position: 'absolute', bottom: 16, left: '50%', transform: 'translateX(-50%)',
         zIndex: 400, padding: '10px 16px', borderRadius: 999, border: '1.5px solid',
         fontSize: 12.5, fontWeight: 600, cursor: 'pointer',
         display: 'flex', alignItems: 'center', gap: 8,
         boxShadow: '0 4px 12px rgba(0,0,0,0.4)' },
};

const pickerStyles = {
  scrim: { position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.7)', zIndex: 100,
           backdropFilter: 'blur(4px)' },
  modal: { position: 'fixed', inset: '5% 5%', maxWidth: 720, margin: '0 auto', zIndex: 101,
           background: '#0d0d10', border: '1px solid #26262e', borderRadius: 14,
           display: 'flex', flexDirection: 'column', overflow: 'hidden' },
  head: { padding: '14px 16px', borderBottom: '1px solid #1f1f25',
          display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 12 },
  title: { fontSize: 14, fontWeight: 600, color: '#fafaf7' },
  sub: { fontSize: 11.5, color: '#7a7a84', marginTop: 2 },
  close: { width: 26, height: 26, borderRadius: 6, border: '1px solid #2a2a31',
           background: '#15151a', color: '#a8a8b2', cursor: 'pointer', display: 'flex',
           alignItems: 'center', justifyContent: 'center', padding: 0 },
  map: { flex: 1, minHeight: 320 },
  footer: { padding: '12px 14px', borderTop: '1px solid #1f1f25',
            display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 10 },
  locBtn: { padding: '7px 10px', background: '#15151a', border: '1px solid #2a2a31',
            borderRadius: 8, color: '#fafaf7', fontSize: 12, fontWeight: 500,
            cursor: 'pointer', display: 'flex', alignItems: 'center' },
  coord: { fontSize: 11, color: '#7a7a84', flex: 1, textAlign: 'center' },
  confirm: { padding: '8px 14px', background: 'var(--accent)', border: 0,
             borderRadius: 8, color: '#0a0a0c', fontSize: 12.5, fontWeight: 600 },
};

Object.assign(window, { MapView, LocationPicker });
