import { useState, useEffect, useCallback, useRef, useMemo } from "react";
import { supabase } from "@/integrations/supabase/client";
import { Checkbox } from "@/components/ui/checkbox";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import { Label } from "@/components/ui/label";
import { useToast } from "@/hooks/use-toast";
import { useIsMobile } from "@/hooks/use-mobile";
import { CheckSquare, FileText, X, Download, Camera, Loader2, Trash2, Map as MapIcon, List as ListIcon } from "lucide-react";
import { jsPDF } from "jspdf";
import { logStageChange, logStatusChange, logViolationCreated } from "@/lib/violationTimelineLogger";

const STAGE_ORDER = ["First Notice", "Second Notice", "Third & Final Notice"];
function getNextStage(current: string | null): string | null {
  if (!current) return "First Notice";
  const idx = STAGE_ORDER.indexOf(current);
  if (idx < 0) return "First Notice";
  if (idx >= STAGE_ORDER.length - 1) return null; // already at max
  return STAGE_ORDER[idx + 1];
}

// ── Brand tokens ──
const B = {
  blue: "#00529b", blueHover: "#003f7a", blueLight: "#e8f1f9", blueMid: "#cde0f3",
  darkGray: "#343a40", midGray: "#6c757d", lightGray: "#adb5bd",
  border: "#dee2e6", header: "#e9ecef", bg: "#f8f9fa", white: "#ffffff",
  red: "#dc3545", redLight: "#fff5f5", amber: "#f59e0b", amberLight: "#fffbeb",
  green: "#198754", greenLight: "#f0fdf4",
};

const DEFAULT_CENTER: [number, number] = [28.0836, -80.6081];

import { L, geocodeAddress, reverseGeocode, circleDivIcon, staticSatelliteUrl, addHybridSatelliteLayer } from "@/lib/leafletMaps";

const getIconForCategory = (cat: string) => {
  const c = (cat || "").toLowerCase();
  if (c.includes("lawn") || c.includes("vegetation") || c.includes("weed")) return "🌿";
  if (c.includes("vehicle") || c.includes("car")) return "🚗";
  if (c.includes("trash") || c.includes("dumping")) return "🗑️";
  if (c.includes("structural") || c.includes("damage")) return "🏚️";
  if (c.includes("pressure") || c.includes("wash")) return "💧";
  if (c.includes("fence") || c.includes("wall")) return "🚧";
  if (c.includes("paint") || c.includes("exterior")) return "🎨";
  return "⚠️";
};

const formatAssociationGeocodeHint = (association: any) =>
  [association?.city, association?.state, association?.zip].filter(Boolean).join(", ");

const getUnitGeocodeAddress = (unit: any) =>
  unit?.property_address?.trim() || unit?.address?.trim() || unit?.unit_number?.trim() || "";

const getUnitGeocodeCandidates = (unit: any, associationAddress?: string | null) => {
  const rawAddress = getUnitGeocodeAddress(unit);
  const unitNumber = unit?.unit_number?.trim();
  const baseAssociationAddress = associationAddress?.trim();
  const looksSuspicious =
    ((rawAddress.match(/#/g) || []).length > 1) ||
    ((rawAddress.match(/\b(unit|apt|suite)\b/gi) || []).length > 1);

  const candidates = [
    ...(looksSuspicious ? [] : [rawAddress]),
    ...(baseAssociationAddress && unitNumber
      ? [`${baseAssociationAddress}, #${unitNumber}`, `${baseAssociationAddress} Unit ${unitNumber}`]
      : []),
    rawAddress,
  ]
    .map((value) => value?.replace(/\s+/g, " ").trim())
    .filter(Boolean);

  return Array.from(new Set(candidates));
};

const inputStyle: React.CSSProperties = { width: "100%", background: B.white, border: `1px solid ${B.border}`, borderRadius: 6, padding: "8px 11px", color: B.darkGray, fontSize: 13, outline: "none", fontFamily: "inherit", boxSizing: "border-box" as const, transition: "border-color .15s" };
const btnPrimary: React.CSSProperties = { background: B.blue, border: "none", borderRadius: 6, color: B.white, fontWeight: 600, cursor: "pointer", fontFamily: "inherit", transition: "background .15s" };
const btnGhost: React.CSSProperties = { background: B.white, border: `1px solid ${B.border}`, borderRadius: 6, color: B.midGray, fontWeight: 500, cursor: "pointer", fontFamily: "inherit", transition: "all .15s" };
const card: React.CSSProperties = { background: B.white, border: `1px solid ${B.border}`, borderRadius: 8 };

function InspBadge({ count, color = B.red }: { count: number; color?: string }) {
  if (!count) return null;
  return <span style={{ background: color, color: B.white, borderRadius: 99, fontSize: 10, fontWeight: 700, padding: "1px 7px", marginLeft: 5, letterSpacing: 0.3 }}>{count}</span>;
}

function Spinner({ size = 22, color = B.blue }: { size?: number; color?: string }) {
  return <div style={{ width: size, height: size, border: `2.5px solid ${color}22`, borderTopColor: color, borderRadius: "50%", animation: "spin .8s linear infinite", flexShrink: 0 }} />;
}

function SectionLabel({ children }: { children: React.ReactNode }) {
  return <div style={{ fontSize: 10, fontWeight: 700, color: B.lightGray, letterSpacing: 1.5, textTransform: "uppercase", marginBottom: 6 }}>{children}</div>;
}

// ── Street View Panel (replaced with static satellite image — Street View not available without Google Maps) ──
function StreetViewPanel({ address, height = "100%" }: { address: string; height?: string }) {
  const [status, setStatus] = useState<"loading" | "ok" | "unavailable">("loading");
  const [imgUrl, setImgUrl] = useState<string | null>(null);

  useEffect(() => {
    if (!address) { setStatus("unavailable"); return; }
    setStatus("loading"); setImgUrl(null);
    let cancelled = false;
    (async () => {
      const geo = await geocodeAddress(address);
      if (cancelled) return;
      if (!geo) { setStatus("unavailable"); return; }
      setImgUrl(staticSatelliteUrl(geo.lat, geo.lng, 800, 500, 19));
      setStatus("ok");
    })();
    return () => { cancelled = true; };
  }, [address]);

  return (
    <div style={{ position: "relative", width: "100%", height, borderRadius: 8, overflow: "hidden", border: `1px solid ${B.border}`, background: B.bg }}>
      {imgUrl && status === "ok" && (
        <img src={imgUrl} alt={`Satellite view of ${address}`} style={{ width: "100%", height: "100%", objectFit: "cover" }} />
      )}
      {status === "loading" && <div style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 10, background: B.bg, zIndex: 5 }}><Spinner /><span style={{ fontSize: 12, color: B.midGray }}>Loading satellite view…</span></div>}
      {status === "unavailable" && <div style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 10, background: B.bg, zIndex: 5 }}><span style={{ fontSize: 32 }}>🛰️</span><span style={{ fontSize: 13, color: B.midGray, textAlign: "center", padding: "0 20px" }}>Satellite view not available</span></div>}
      {status === "ok" && (
        <div style={{ position: "absolute", bottom: 6, left: 6, background: "rgba(0,0,0,.6)", color: "#fff", fontSize: 10, padding: "2px 6px", borderRadius: 3 }}>🛰️ Satellite</div>
      )}
    </div>
  );
}

// ── Interactive Map (Leaflet + OpenStreetMap, no API key needed) ──
function InteractiveMap({ units, violationsByUnit, selectedUnit, onSelectUnit, onDropPin, onLogViolation, onResolve, onEscalate, centerAddress, associationId, associationAddress, geocodeHint }: any) {
  const containerRef = useRef<HTMLDivElement>(null);
  const mapRef = useRef<L.Map | null>(null);
  const layerRef = useRef<L.LayerGroup | null>(null);
  const markersRef = useRef<Map<string, L.Marker>>(new Map());
  const geoCacheRef = useRef<Map<string, { lat: number; lng: number } | null>>(new Map());
  const dropPinRef = useRef<L.Marker | null>(null);
  const dropModeRef = useRef(false);
  const [mapsLoaded, setMapsLoaded] = useState(false);
  const [mapsError, setMapsError] = useState<string | null>(null);
  const [dropMode, setDropMode] = useState(false);
  const [dropAddress, setDropAddress] = useState<string | null>(null);
  const [dropLoading, setDropLoading] = useState(false);
  const [savedViewToast, setSavedViewToast] = useState(false);
  const lockedViewKey = associationId ? `inspections_locked_view_${associationId}` : null;
  const hasLockedView = !!(lockedViewKey && typeof window !== "undefined" && localStorage.getItem(lockedViewKey));

  useEffect(() => { dropModeRef.current = dropMode; }, [dropMode]);

  // Init map once
  useEffect(() => {
    if (!containerRef.current || mapRef.current) return;
    try {
      const map = L.map(containerRef.current, {
        center: DEFAULT_CENTER,
        zoom: 13,
        zoomControl: true,
        preferCanvas: true,
      });
      // Hybrid satellite + labels (Mapbox when available, Esri fallback).
      void addHybridSatelliteLayer(map);

      // Plain layer group (no clustering) — markers render directly on the map.
      const layer = L.layerGroup().addTo(map);
      layerRef.current = layer;
      mapRef.current = map;
      setMapsLoaded(true);

      map.on("click", async (e: L.LeafletMouseEvent) => {
        if (!dropModeRef.current) return;
        const { lat, lng } = e.latlng;
        if (dropPinRef.current) dropPinRef.current.remove();
        dropPinRef.current = L.marker([lat, lng], {
          icon: circleDivIcon({ color: B.amber, size: 22 }),
          zIndexOffset: 1000,
        }).addTo(map);
        setDropLoading(true); setDropAddress(null);
        const addr = await reverseGeocode(lat, lng);
        setDropAddress(addr || `${lat.toFixed(5)}, ${lng.toFixed(5)}`);
        setDropLoading(false);
      });
    } catch (e: any) {
      setMapsError(e.message || "Failed to load map");
    }

    return () => {
      if (mapRef.current) { mapRef.current.remove(); mapRef.current = null; }
      layerRef.current = null;
      markersRef.current.clear();
    };
  }, []);

  // Apply locked view (per-association saved center+zoom). Falls back to
  // panning to the association's address when no saved view exists.
  useEffect(() => {
    if (!mapRef.current) return;
    if (lockedViewKey) {
      try {
        const raw = localStorage.getItem(lockedViewKey);
        if (raw) {
          const v = JSON.parse(raw);
          if (typeof v?.lat === "number" && typeof v?.lng === "number") {
            mapRef.current.setView([v.lat, v.lng], typeof v.zoom === "number" ? v.zoom : 16);
            return;
          }
        }
      } catch { /* ignore */ }
    }
    if (centerAddress) {
      geocodeAddress(centerAddress).then(g => { if (g && mapRef.current) { mapRef.current.setView([g.lat, g.lng], 14); } });
    }
  }, [centerAddress, lockedViewKey]);

  // Pan to selected unit (still respects user clicking sidebar)
  const resolveUnitGeo = useCallback(async (unit: any) => {
    for (const candidate of getUnitGeocodeCandidates(unit, associationAddress)) {
      const geocodeCacheKey = `${candidate}__${geocodeHint || ""}`;
      let geo = geoCacheRef.current.get(geocodeCacheKey);
      if (geo === undefined) {
        geo = await geocodeAddress(candidate, { context: geocodeHint });
        geoCacheRef.current.set(geocodeCacheKey, geo || null);
      }
      if (geo) return geo;
    }
    return null;
  }, [associationAddress, geocodeHint]);

  // Auto-pan to selected unit disabled — base address pins removed; users drop pins manually.

  // Render unit markers directly on the map (no clustering). Geocoding results
  // are cached per address so re-renders due to selection changes don't re-fetch.
  // Markers are diffed against the existing set so only changed unit pins are
  // recreated; selection-only changes just restyle in place.
  // Render pins ONLY for units that have at least one open violation. Clean
  // base map otherwise — manual drop-pins still work via the drop-pin tool.
  useEffect(() => {
    if (!mapsLoaded || !mapRef.current || !layerRef.current) return;
    const map = mapRef.current;
    const layer = layerRef.current;
    let cancelled = false;

    const isOpen = (v: any) => {
      const s = String(v?.status || "").toLowerCase();
      return s === "open" || s === "" || s === "new" || s === "active";
    };

    const unitsWithOpen = units.filter((u: any) => {
      if (!u?.id || !getUnitGeocodeAddress(u)) return false;
      const list = violationsByUnit?.[u.id] || [];
      return list.some(isOpen);
    });

    const desiredIds = new Set<string>(unitsWithOpen.map((u: any) => String(u.id)));

    markersRef.current.forEach((marker, id) => {
      if (!desiredIds.has(id)) {
        try { layer.removeLayer(marker); } catch { /* ignore */ }
        markersRef.current.delete(id);
      }
    });

    const buildPopup = (u: any, openViols: any[]) => {
      const violList = openViols.map((v: any) => {
        const nextStage = getNextStage(v.stage || v.notice_level || null);
        const escalateBtn = nextStage
          ? `<button class="iw-escalate-btn" data-vid="${v.id}" data-current="${v.stage || v.notice_level || ''}" data-next="${nextStage}" style="background:#f59e0b;color:white;border:none;border-radius:4px;padding:2px 8px;font-size:10px;font-weight:600;cursor:pointer;white-space:nowrap" title="Escalate to ${nextStage}">⬆ Escalate</button>`
          : `<span style="font-size:9px;color:#6c757d;white-space:nowrap">Max stage</span>`;
        const lastTs = v.updated_at || v.created_at;
        let lastLabel = "";
        let lastColor = "#6c757d";
        if (lastTs) {
          const d = new Date(lastTs);
          const days = Math.floor((Date.now() - d.getTime()) / 86400000);
          const dateStr = d.toLocaleDateString("en-US", { month: "numeric", day: "numeric", year: "2-digit" });
          lastLabel = `Updated ${dateStr} (${days}d)`;
          if (days >= 30) lastColor = "#dc3545";
          else if (days >= 14) lastColor = "#f59e0b";
        }
        return `<div style="display:flex;align-items:center;gap:6px;padding:4px 0;font-size:12px;color:#343a40;border-bottom:1px solid #f0f0f0">
          <span>${getIconForCategory(v.category || v.violation_type || "")}</span>
          <span style="flex:1;display:flex;flex-direction:column;gap:1px">
            <span>${v.category || v.violation_type || v.title || "Violation"}</span>
            ${lastLabel ? `<span style="font-size:9px;color:${lastColor};font-weight:600">${lastLabel}</span>` : ""}
          </span>
          <span style="font-size:9px;color:#6c757d;background:#f0f0f0;border-radius:3px;padding:1px 5px">${v.stage || v.notice_level || "New"}</span>
          ${escalateBtn}
          <button class="iw-resolve-btn" data-vid="${v.id}" style="background:#198754;color:white;border:none;border-radius:4px;padding:2px 8px;font-size:10px;font-weight:600;cursor:pointer;white-space:nowrap">✓ Resolve</button>
        </div>`;
      }).join("");
      const open = openViols.length;
      return `
        <div style="font-family:Inter,system-ui,sans-serif;min-width:240px;max-width:320px">
          <div style="font-size:14px;font-weight:700;color:#343a40;margin-bottom:2px">${u.address || ""}</div>
          ${u.owner_name ? `<div style="font-size:11px;color:#6c757d;margin-bottom:8px">👤 ${u.owner_name}</div>` : ""}
          <div style="font-size:10px;font-weight:700;color:#dc3545;text-transform:uppercase;letter-spacing:1px;margin-bottom:4px">⚠️ ${open} Open Violation${open !== 1 ? "s" : ""}</div>
          <div style="margin-bottom:8px;max-height:140px;overflow-y:auto">${violList}</div>
          <button id="iw-log-btn-${u.id}" style="width:100%;background:#00529b;color:white;border:none;border-radius:8px;padding:12px 0;font-weight:700;font-size:14px;cursor:pointer;font-family:inherit;touch-action:manipulation">📸 + Log Violation</button>
        </div>
      `;
    };

    const attachPopupHandlers = (u: any) => {
      const btn = document.getElementById(`iw-log-btn-${u.id}`);
      if (btn) btn.addEventListener("click", () => { onLogViolation(u.address); map.closePopup(); });
      document.querySelectorAll(".iw-resolve-btn").forEach((el) => {
        el.addEventListener("click", async (e) => {
          e.stopPropagation();
          const vid = (el as HTMLElement).dataset.vid;
          if (!vid) return;
          (el as HTMLButtonElement).disabled = true;
          (el as HTMLButtonElement).textContent = "...";
          const { error } = await supabase.from("violations").update({ status: "resolved" }).eq("id", vid);
          if (!error) {
            logStatusChange(vid, "open", "resolved");
            (el as HTMLElement).parentElement!.style.opacity = "0.4";
            (el as HTMLButtonElement).textContent = "Resolved";
            (el as HTMLButtonElement).style.background = "#6c757d";
            onResolve?.();
          } else {
            (el as HTMLButtonElement).textContent = "Error";
            (el as HTMLButtonElement).disabled = false;
          }
        });
      });
      document.querySelectorAll(".iw-escalate-btn").forEach((el) => {
        el.addEventListener("click", async (e) => {
          e.stopPropagation();
          const vid = (el as HTMLElement).dataset.vid;
          const currentStage = (el as HTMLElement).dataset.current || null;
          const nextStage = (el as HTMLElement).dataset.next;
          if (!vid || !nextStage) return;
          (el as HTMLButtonElement).disabled = true;
          (el as HTMLButtonElement).textContent = "...";
          const { error } = await supabase.from("violations").update({ stage: nextStage, notice_level: nextStage }).eq("id", vid);
          if (!error) {
            await logStageChange(vid, currentStage, nextStage);
            (el as HTMLButtonElement).textContent = nextStage;
            (el as HTMLButtonElement).style.background = "#6c757d";
            onEscalate?.();
          } else {
            (el as HTMLButtonElement).textContent = "Error";
            (el as HTMLButtonElement).disabled = false;
          }
        });
      });
    };

    const CONCURRENCY = 6;
    let cursor = 0;
    const newMarkers: L.Marker[] = [];

    const processOne = async () => {
      while (!cancelled) {
        const idx = cursor++;
        if (idx >= unitsWithOpen.length) return;
        const u = unitsWithOpen[idx];
        const id = String(u.id);
        const unitViols = (violationsByUnit?.[u.id] || []) as any[];
        const openViols = unitViols.filter(isOpen);
        const hasPhotos = openViols.some((v: any) => v.photo_url || (Array.isArray(v.photo_urls) && v.photo_urls.length > 0));
        const isSel = selectedUnit?.id === u.id;
        const color = hasPhotos ? B.amber : B.red;
        const houseNum = ((u.address || "").match(/^\d+/) || [])[0] || String(openViols.length);
        const size = isSel ? 32 : houseNum.length <= 3 ? 24 : 28;
        const fontSize = houseNum.length <= 3 ? "10px" : "9px";
        const icon = circleDivIcon({ color, size, label: houseNum, fontSize, selected: isSel });

        const geo = await resolveUnitGeo(u);
        if (cancelled || !geo) continue;

        const existing = markersRef.current.get(id);
        if (existing) {
          existing.setIcon(icon);
          existing.setZIndexOffset(isSel ? 500 : 50);
          existing.setLatLng([geo.lat, geo.lng]);
          existing.setPopupContent(buildPopup(u, openViols));
          continue;
        }

        const marker = L.marker([geo.lat, geo.lng], {
          title: u.address,
          icon,
          zIndexOffset: isSel ? 500 : 50,
        });
        marker.bindPopup(buildPopup(u, openViols), { minWidth: 240, maxWidth: 320 });
        marker.on("click", () => { if (!dropModeRef.current) onSelectUnit(u); });
        marker.on("popupopen", () => attachPopupHandlers(u));
        markersRef.current.set(id, marker);
        newMarkers.push(marker);
      }
    };

    const runners = Array.from({ length: Math.min(CONCURRENCY, unitsWithOpen.length) || 1 }, () => processOne());
    Promise.all(runners).then(() => {
      if (cancelled || !layerRef.current) return;
      newMarkers.forEach(m => layerRef.current!.addLayer(m));
    });

    return () => { cancelled = true; };
  }, [mapsLoaded, units, violationsByUnit, selectedUnit, resolveUnitGeo, onLogViolation, onResolve, onEscalate, onSelectUnit]);

  const cancelDrop = () => {
    setDropMode(false); setDropAddress(null); setDropLoading(false);
    if (dropPinRef.current) { dropPinRef.current.remove(); dropPinRef.current = null; }
  };

  if (mapsError) return (
    <div style={{ flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 14, background: B.bg, padding: 32 }}>
      <span style={{ fontSize: 36 }}>⚠️</span>
      <div style={{ fontSize: 15, fontWeight: 700, color: B.red }}>Map failed to load</div>
      <div style={{ fontSize: 12, color: B.midGray, textAlign: "center", maxWidth: 360 }}>{mapsError}</div>
    </div>
  );

  return (
    <div style={{ flex: 1, position: "relative", overflow: "hidden" }}>
      <div ref={containerRef} style={{ width: "100%", height: "100%", background: B.bg }} />
      {!mapsLoaded && <div style={{ position: "absolute", inset: 0, background: B.bg, display: "flex", alignItems: "center", justifyContent: "center", gap: 12, zIndex: 10 }}><Spinner size={28} /><span style={{ color: B.midGray, fontSize: 13 }}>Loading map…</span></div>}

      {mapsLoaded && (
        <div style={{ position: "absolute", top: 12, right: 12, zIndex: 1000, display: "flex", flexDirection: "column", gap: 8, alignItems: "flex-end" }}>
          {!dropMode ? (
            <>
              <button onClick={() => { setDropMode(true); setDropAddress(null); }}
                style={{ display: "flex", alignItems: "center", gap: 7, padding: "8px 14px", background: B.blue, border: "none", borderRadius: 7, color: B.white, fontWeight: 600, fontSize: 13, cursor: "pointer", boxShadow: "0 2px 10px rgba(0,82,155,.3)", fontFamily: "inherit" }}>
                📍 Drop Pin
              </button>
              {lockedViewKey && (
                <div style={{ display: "flex", gap: 6, background: B.white, border: `1px solid ${B.border}`, borderRadius: 7, padding: 4, boxShadow: "0 2px 8px rgba(0,0,0,.08)" }}>
                  <button
                    onClick={() => {
                      if (!mapRef.current || !lockedViewKey) return;
                      const c = mapRef.current.getCenter();
                      const z = mapRef.current.getZoom();
                      localStorage.setItem(lockedViewKey, JSON.stringify({ lat: c.lat, lng: c.lng, zoom: z }));
                      setSavedViewToast(true);
                      setTimeout(() => setSavedViewToast(false), 1800);
                    }}
                    title="Save the current map center and zoom for this association"
                    style={{ background: "transparent", border: "none", color: B.darkGray, fontWeight: 600, fontSize: 12, padding: "5px 9px", cursor: "pointer", borderRadius: 4, fontFamily: "inherit" }}
                  >
                    💾 Save view
                  </button>
                  {hasLockedView && (
                    <button
                      onClick={() => {
                        if (!lockedViewKey) return;
                        localStorage.removeItem(lockedViewKey);
                        setSavedViewToast(false);
                        // Pan back to association address if available
                        if (centerAddress && mapRef.current) {
                          geocodeAddress(centerAddress).then(g => { if (g && mapRef.current) mapRef.current.setView([g.lat, g.lng], 14); });
                        }
                      }}
                      title="Clear the saved view for this association"
                      style={{ background: "transparent", border: "none", color: B.midGray, fontWeight: 600, fontSize: 12, padding: "5px 9px", cursor: "pointer", borderRadius: 4, fontFamily: "inherit" }}
                    >
                      ✕ Reset
                    </button>
                  )}
                </div>
              )}
              {savedViewToast && (
                <div style={{ background: B.green, color: B.white, fontSize: 11, fontWeight: 600, padding: "5px 10px", borderRadius: 5, boxShadow: "0 2px 8px rgba(0,0,0,.15)" }}>
                  ✓ View saved
                </div>
              )}
            </>
          ) : (
            <div style={{ background: B.white, border: `1.5px solid ${B.amber}`, borderRadius: 10, padding: "14px 16px", width: 270, boxShadow: "0 4px 20px rgba(0,0,0,.12)" }}>
              <div style={{ fontSize: 10, fontWeight: 700, color: B.amber, letterSpacing: 1.5, textTransform: "uppercase", marginBottom: 5 }}>📍 Drop Pin Mode</div>
              <div style={{ fontSize: 12, color: B.midGray, marginBottom: 10, lineHeight: 1.5 }}>Click anywhere on the map to place a violation pin.</div>
              {dropLoading && <div style={{ display: "flex", alignItems: "center", gap: 7, marginBottom: 8 }}><Spinner size={13} color={B.amber} /><span style={{ fontSize: 12, color: B.amber }}>Getting address…</span></div>}
              {dropAddress && !dropLoading && <div style={{ marginBottom: 10 }}><SectionLabel>Address</SectionLabel><div style={{ fontSize: 12, fontWeight: 600, color: B.darkGray, background: B.bg, borderRadius: 6, padding: "7px 10px", lineHeight: 1.4, border: `1px solid ${B.border}` }}>{dropAddress}</div></div>}
              <div style={{ display: "flex", gap: 7 }}>
                <button onClick={cancelDrop} style={{ ...btnGhost, flex: 1, padding: "7px 0", fontSize: 12 }}>Cancel</button>
                <button onClick={() => { if (dropAddress) { onDropPin(dropAddress); cancelDrop(); } }} disabled={!dropAddress || dropLoading}
                  style={{ ...btnPrimary, flex: 2, padding: "7px 0", fontSize: 12, opacity: dropAddress && !dropLoading ? 1 : 0.45 }}>
                  Log Violation Here →
                </button>
              </div>
            </div>
          )}
        </div>
      )}

      {mapsLoaded && !dropMode && (
        <div style={{ position: "absolute", top: 12, left: 12, background: "rgba(255,255,255,.92)", border: `1px solid ${B.border}`, borderRadius: 7, padding: "6px 11px", fontSize: 11, color: B.midGray, pointerEvents: "none", zIndex: 1000 }}>
          Click a pin to inspect · 📍 Drop Pin to log anywhere
        </div>
      )}

      {mapsLoaded && (
        <div style={{ position: "absolute", bottom: 20, left: 12, background: "rgba(255,255,255,.95)", border: `1px solid ${B.border}`, borderRadius: 8, padding: "10px 13px", pointerEvents: "none", zIndex: 1000 }}>
          <div style={{ fontSize: 9, fontWeight: 700, color: B.lightGray, letterSpacing: 1, marginBottom: 6, textTransform: "uppercase" }}>Legend</div>
          {[[B.blue, "No violations"], [B.red, "Open violations"], [B.amber, "Drop pin"]].map(([c, l]) => (
            <div key={l} style={{ display: "flex", alignItems: "center", gap: 7, marginBottom: 4 }}>
              <div style={{ width: 9, height: 9, borderRadius: "50%", background: c, flexShrink: 0 }} />
              <span style={{ fontSize: 11, color: B.darkGray }}>{l}</span>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ── Violation Form ──
function ViolationForm({ address, associationId, existing, onCancel, onSaved, violationTypes, typesLoading }: any) {
  const [selected, setSelected] = useState<string[]>([]);
  const [notes, setNotes] = useState("");
  const [addrEdit, setAddrEdit] = useState(address);
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [photos, setPhotos] = useState<{ url: string; name: string }[]>([]);
  const [uploading, setUploading] = useState(false);
  const fileInputRef = useRef<HTMLInputElement>(null);
  const toggle = (id: string) => setSelected(p => p.includes(id) ? p.filter(x => x !== id) : [...p, id]);

  const handlePhotoUpload = async (files: FileList | null) => {
    if (!files || files.length === 0) return;
    setUploading(true);
    const newPhotos: { url: string; name: string }[] = [];
    for (const file of Array.from(files)) {
      if (file.size > 10 * 1024 * 1024) continue;
      const ext = file.name.split(".").pop();
      const path = `inspections/${Date.now()}_${Math.random().toString(36).substring(7)}.${ext}`;
      const { error: upErr } = await supabase.storage.from("violation-photos").upload(path, file);
      if (upErr) { console.error("Upload error:", upErr); continue; }
      const { data: { publicUrl } } = supabase.storage.from("violation-photos").getPublicUrl(path);
      newPhotos.push({ url: publicUrl, name: file.name });
    }
    setPhotos(prev => [...prev, ...newPhotos]);
    setUploading(false);
    if (fileInputRef.current) fileInputRef.current.value = "";
  };

  const removePhoto = (idx: number) => setPhotos(prev => prev.filter((_, i) => i !== idx));

  const save = async () => {
    if (!selected.length) return;
    setSaving(true); setError(null);
    const dateStr = new Date().toISOString().split("T")[0];
    const photoUrls = photos.map(p => p.url);
    try {
      const toInsert: any[] = [];
      const toEscalate: { id: string; currentStage: string | null }[] = [];

      for (const id of selected) {
        const vt = (violationTypes || []).find((v: any) => v.id === id);
        const category = vt?.category || id;
        const match = (existing || []).find((ex: any) =>
          (ex.category === category || ex.violation_type === category) &&
          (ex.status === "open" || ex.status === "Open" || ex.status === "recommended_for_fining")
        );
        if (match) {
          toEscalate.push({ id: match.id, currentStage: match.stage || match.notice_level || null });
        } else {
          toInsert.push({
            association_id: associationId,
            address: addrEdit,
            violation_type: category,
            title: `${category} - ${addrEdit}`,
            category: vt?.category || null,
            status: "open",
            stage: "First Notice",
            notice_level: "First Notice",
            notes: notes || null,
            violation_date: dateStr,
            ...(photoUrls.length > 0 ? { photo_url: photoUrls[0], photo_urls: photoUrls } : {}),
          });
        }
      }

      if (toInsert.length > 0) {
        const { data: inserted, error: err } = await supabase.from("violations").insert(toInsert).select("id, address, violation_type, stage");
        if (err) throw err;
        for (const ins of inserted || []) {
          logViolationCreated(ins.id, { address: ins.address, violationType: ins.violation_type, noticeLevel: ins.stage });
        }
      }

      for (const esc of toEscalate) {
        const nextStage = getNextStage(esc.currentStage);
        if (nextStage) {
          const updateData: any = { stage: nextStage, notice_level: nextStage };
          if (notes) updateData.notes = notes;
          if (photoUrls.length > 0) { updateData.photo_url = photoUrls[0]; updateData.photo_urls = photoUrls; }
          const { error: err } = await supabase.from("violations").update(updateData).eq("id", esc.id);
          if (err) throw err;
          await logStageChange(esc.id, esc.currentStage, nextStage);
        } else if (notes || photoUrls.length > 0) {
          const updateData: any = {};
          if (notes) updateData.notes = notes;
          if (photoUrls.length > 0) { updateData.photo_url = photoUrls[0]; updateData.photo_urls = photoUrls; }
          await supabase.from("violations").update(updateData).eq("id", esc.id);
        }
      }

      onSaved();
    } catch (e: any) { setError(e.message); setSaving(false); }
  };

  return (
    <div style={{ display: "flex", flexDirection: "column", height: "100%", background: B.white, minHeight: 0 }}>
      <div style={{ padding: "14px 18px 12px", borderBottom: `1px solid ${B.border}`, background: B.bg, flexShrink: 0 }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 12 }}>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: 1.5, color: B.blue, textTransform: "uppercase", marginBottom: 7 }}>🏠 Log New Violation</div>
            <input value={addrEdit} onChange={e => setAddrEdit(e.target.value)} style={{ ...inputStyle, fontWeight: 600, fontSize: 13 }} />
            <div style={{ fontSize: 10, color: B.lightGray, marginTop: 4 }}>Edit address if needed</div>
            {existing.length > 0 && <div style={{ marginTop: 6, fontSize: 11, color: B.red, fontWeight: 600 }}>⚠️ {existing.length} open violation{existing.length !== 1 ? "s" : ""} already on file — matching types will be escalated, not duplicated</div>}
          </div>
          {onCancel && <button onClick={onCancel} style={{ ...btnGhost, width: 30, height: 30, padding: 0, display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, fontSize: 14 }}>✕</button>}
        </div>
      </div>

      <div style={{ overflowY: "auto", padding: "14px 18px", flex: 1, minHeight: 0 }}>
        <SectionLabel>Select Violations</SectionLabel>
        {typesLoading ? (
          <div style={{ fontSize: 13, color: B.midGray, padding: "10px 0", display: "flex", alignItems: "center", gap: 8 }}><Spinner size={16} /> Loading violation types...</div>
        ) : (!violationTypes || violationTypes.length === 0) ? (
          <div style={{ fontSize: 13, color: B.midGray, padding: "10px 0", fontStyle: "italic" }}>No violation types configured for this association.</div>
        ) : (
          <div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
            {violationTypes.map((v: any) => {
              const on = selected.includes(v.id);
              return (
                <div key={v.id} onClick={() => toggle(v.id)} style={{ display: "flex", alignItems: "center", gap: 10, padding: "8px 11px", borderRadius: 7, background: on ? B.blueLight : B.bg, border: `1px solid ${on ? B.blue : B.border}`, cursor: "pointer", userSelect: "none", transition: "all .12s" }}>
                  <div style={{ width: 17, height: 17, borderRadius: 4, border: `2px solid ${on ? B.blue : B.lightGray}`, background: on ? B.blue : "transparent", display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0 }}>
                    {on && <span style={{ color: B.white, fontSize: 10, fontWeight: 900, lineHeight: 1 }}>✓</span>}
                  </div>
                  <span style={{ fontSize: 14 }}>{getIconForCategory(v.category)}</span>
                  <span style={{ fontSize: 13, color: on ? B.blue : B.darkGray, fontWeight: on ? 600 : 400 }}>{v.category || "Unnamed Type"}</span>
                </div>
              );
            })}
          </div>
        )}

        {/* Photo Upload Section */}
        <div style={{ marginTop: 14 }}>
          <SectionLabel>Photos</SectionLabel>
          <input ref={fileInputRef} type="file" accept="image/*" multiple capture="environment" onChange={e => handlePhotoUpload(e.target.files)} style={{ display: "none" }} />
          <div style={{ display: "flex", flexWrap: "wrap", gap: 8, marginBottom: photos.length > 0 ? 8 : 0 }}>
            {photos.map((p, i) => (
              <div key={i} style={{ position: "relative", width: 64, height: 64, borderRadius: 6, overflow: "hidden", border: `1px solid ${B.border}` }}>
                <img src={p.url} alt={p.name} style={{ width: "100%", height: "100%", objectFit: "cover" }} />
                <button onClick={() => removePhoto(i)} style={{ position: "absolute", top: 2, right: 2, width: 18, height: 18, borderRadius: "50%", background: "rgba(0,0,0,.6)", border: "none", color: B.white, fontSize: 10, cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center", padding: 0 }}>✕</button>
              </div>
            ))}
          </div>
          <button onClick={() => fileInputRef.current?.click()} disabled={uploading}
            style={{ ...btnGhost, padding: "7px 14px", fontSize: 12, display: "flex", alignItems: "center", gap: 6 }}>
            {uploading ? <><Loader2 size={14} style={{ animation: "spin .8s linear infinite" }} /> Uploading…</> : <><Camera size={14} /> {photos.length > 0 ? "Add More Photos" : "Take / Upload Photos"}</>}
          </button>
          {photos.length > 0 && (
            <div style={{ marginTop: 4, fontSize: 11, color: B.green, fontWeight: 600, display: "flex", alignItems: "center", gap: 4 }}>
              <Camera size={12} /> {photos.length} photo{photos.length !== 1 ? "s" : ""} attached
            </div>
          )}
        </div>

        <div style={{ marginTop: 14 }}>
          <SectionLabel>Inspector Notes</SectionLabel>
          <textarea value={notes} onChange={e => setNotes(e.target.value)} placeholder="Additional observations…" style={{ ...inputStyle, minHeight: 70, resize: "vertical" }} />
        </div>
        {error && <div style={{ marginTop: 8, padding: "8px 11px", background: "#fff5f5", border: `1px solid ${B.red}`, borderRadius: 6, fontSize: 12, color: B.red }}>Error: {error}</div>}
      </div>

      <div style={{ padding: "10px 18px", borderTop: `1px solid ${B.border}`, display: "flex", gap: 8, justifyContent: "flex-end", alignItems: "center", flexShrink: 0, background: B.bg }}>
        {photos.length > 0 && <span style={{ fontSize: 11, color: B.green, marginRight: "auto", display: "flex", alignItems: "center", gap: 4 }}><Camera size={12} /> {photos.length}</span>}
        {onCancel && <button onClick={onCancel} style={{ ...btnGhost, padding: "7px 16px", fontSize: 13 }}>Cancel</button>}
        <button onClick={save} disabled={!selected.length || saving || typesLoading}
          style={{ ...btnPrimary, padding: "7px 18px", fontSize: 13, opacity: !selected.length || saving || typesLoading ? 0.5 : 1, display: "flex", alignItems: "center", gap: 7 }}>
          {saving ? <><Spinner size={13} color={B.white} />Saving…</> : selected.length ? `Log ${selected.length} Violation${selected.length !== 1 ? "s" : ""}` : "Select violations"}
        </button>
      </div>
    </div>
  );
}

// ── Violation Popup ──
function ViolationPopup({ address, associationId, existing, onClose, onSaved, violationTypes, typesLoading }: any) {
  const isMobile = useIsMobile();
  return (
    <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "rgba(52,58,64,.5)", backdropFilter: "blur(4px)", display: "flex", alignItems: isMobile ? "stretch" : "center", justifyContent: "center", zIndex: 200, padding: isMobile ? 0 : 16 }}>
      <div onClick={e => e.stopPropagation()} style={{ ...card, width: isMobile ? "100%" : "min(520px,96vw)", height: isMobile ? "100%" : "auto", maxHeight: isMobile ? "100%" : "88vh", overflow: "hidden", boxShadow: "0 8px 40px rgba(0,0,0,.18)", borderRadius: isMobile ? 0 : 8 }}>
        <ViolationForm address={address} associationId={associationId} existing={existing} onCancel={onClose} onSaved={onSaved} violationTypes={violationTypes} typesLoading={typesLoading} />
      </div>
    </div>
  );
}

// ── Full-Screen Inspection ──
function FullScreenInspection({ unit, associationId, existing, onClose, onSaved, violationTypes, typesLoading }: any) {
  useEffect(() => { const h = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); }; window.addEventListener("keydown", h); return () => window.removeEventListener("keydown", h); }, [onClose]);
  return (
    <div style={{ position: "fixed", inset: 0, zIndex: 400, background: B.bg, display: "flex", flexDirection: "column", fontFamily: "inherit" }}>
      <div style={{ background: B.white, borderBottom: `1px solid ${B.border}`, padding: "10px 18px", display: "flex", alignItems: "center", justifyContent: "space-between", flexShrink: 0 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <div style={{ width: 32, height: 32, background: B.blue, borderRadius: 7, display: "flex", alignItems: "center", justifyContent: "center", fontSize: 16 }}>🏠</div>
          <div>
            <div style={{ fontSize: 13, fontWeight: 700, color: B.darkGray }}>Property Inspection</div>
            <div style={{ fontSize: 11, color: B.blue, fontWeight: 600 }}>{unit.address}</div>
          </div>
          {unit.owner_name && <div style={{ marginLeft: 8, fontSize: 12, color: B.midGray, background: B.bg, border: `1px solid ${B.border}`, borderRadius: 6, padding: "4px 10px" }}>👤 {unit.owner_name}</div>}
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          <span style={{ fontSize: 11, color: B.lightGray }}>Press Esc to close</span>
          <button onClick={onClose} style={{ ...btnGhost, padding: "6px 13px", fontSize: 13 }}>✕ Close</button>
        </div>
      </div>
      <div style={{ flex: 1, display: "flex", overflow: "hidden", minHeight: 0 }}>
        <div style={{ flex: 1, display: "flex", flexDirection: "column", overflow: "hidden", borderRight: `1px solid ${B.border}` }}>
          <div style={{ padding: "9px 14px", borderBottom: `1px solid ${B.border}`, background: B.header, flexShrink: 0 }}><SectionLabel>📍 Street View</SectionLabel></div>
          <div style={{ flex: 1, padding: 12, overflow: "hidden", minHeight: 0, background: B.bg }}><StreetViewPanel address={unit.address} height="100%" /></div>
        </div>
        <div style={{ width: 440, flexShrink: 0, display: "flex", flexDirection: "column", overflow: "hidden", minHeight: 0 }}>
          <ViolationForm address={unit.address} associationId={associationId} existing={existing} onSaved={onSaved} violationTypes={violationTypes} typesLoading={typesLoading} />
        </div>
      </div>
    </div>
  );
}

// ── Main Inspections Page ──
export default function InspectionsPage() {
  const { toast } = useToast();
  const isMobile = useIsMobile();
  const [mobileView, setMobileView] = useState<"map" | "list">("map");
  const [associations, setAssociations] = useState<any[]>([]);
  const [selectedAssoc, setSelectedAssoc] = useState<any>(null);
  const [units, setUnits] = useState<any[]>([]);
  const [violations, setViolations] = useState<any[]>([]);
  const [loading, setLoading] = useState(false);
  const [selectedUnit, setSelectedUnit] = useState<any>(null);
  const [popupAddress, setPopupAddress] = useState<string | null>(null);
  const [inspectUnit, setInspectUnit] = useState<any>(null);
  const [searchQ, setSearchQ] = useState("");
  const [filter, setFilter] = useState("all");

  const [violationTypes, setViolationTypes] = useState<any[]>([]);
  const [typesLoading, setTypesLoading] = useState(false);

  const [selectedUnitIds, setSelectedUnitIds] = useState<string[]>([]);
  const [bulkViolations, setBulkViolations] = useState<string[]>([]);
  const [bulkNotes, setBulkNotes] = useState("");
  const [isBulkAssigning, setIsBulkAssigning] = useState(false);

  const [showNoticeModal, setShowNoticeModal] = useState(false);
  const [noticeText, setNoticeText] = useState("");

  useEffect(() => { supabase.from("associations").select("id,name,address,city,state,zip").eq("status", "active").order("name").then(({ data }) => setAssociations(data || [])); }, []);

  const refreshViolations = useCallback(async () => {
    if (!selectedAssoc) return;
    const { data } = await supabase.from("violations").select("*").eq("association_id", selectedAssoc.id).order("created_at", { ascending: false });
    setViolations(data || []);
  }, [selectedAssoc]);

  useEffect(() => {
    if (!selectedAssoc) { setUnits([]); setViolations([]); setSelectedUnitIds([]); setViolationTypes([]); return; }
    setLoading(true); setTypesLoading(true); setSelectedUnitIds([]); setBulkViolations([]); setBulkNotes("");

    (async () => {
      const PAGE = 1000;

      // Paginated units fetch — Supabase caps unbounded selects at 1000 rows,
      // so large associations would otherwise be missing pins on the map.
      const allUnits: any[] = [];
      for (let from = 0; ; from += PAGE) {
        const { data, error } = await supabase
          .from("units")
          .select("id, unit_number, address, association_id")
          .eq("association_id", selectedAssoc.id)
          .order("address")
          .range(from, from + PAGE - 1);
        if (error || !data || data.length === 0) break;
        allUnits.push(...data);
        if (data.length < PAGE) break;
      }

      // Paginated violations fetch (same 1000 row limit applies).
      const allViolations: any[] = [];
      for (let from = 0; ; from += PAGE) {
        const { data, error } = await supabase
          .from("violations")
          .select("*")
          .eq("association_id", selectedAssoc.id)
          .order("created_at", { ascending: false })
          .range(from, from + PAGE - 1);
        if (error || !data || data.length === 0) break;
        allViolations.push(...data);
        if (data.length < PAGE) break;
      }

      const { data: types } = await supabase
        .from("violation_types")
        .select("id, category")
        .eq("association_id", selectedAssoc.id)
        .order("category");

      const unitData = allUnits.map((unit: any) => ({ ...unit, property_address: unit.address || unit.unit_number }));
      setViolations(allViolations);
      setViolationTypes(types || []);

      // Fetch owners in chunks — large `.in()` lists can hit URL length limits.
      if (unitData.length > 0) {
        const unitIds = unitData.map((x: any) => x.id);
        const ownerMap: Record<string, { name: string; property_address: string | null }> = {};
        const CHUNK = 200;
        for (let i = 0; i < unitIds.length; i += CHUNK) {
          const chunk = unitIds.slice(i, i + CHUNK);
          const { data: owners } = await supabase
            .from("owners")
            .select("id, first_name, last_name, unit_id, property_address")
            .eq("status", "active")
            .in("unit_id", chunk);
          (owners || []).forEach((o: any) => {
            ownerMap[o.unit_id] = {
              name: `${o.first_name || ""} ${o.last_name || ""}`.trim(),
              property_address: o.property_address,
            };
          });
        }
        setUnits(unitData.map((u: any) => ({
          ...u,
          owner_name: ownerMap[u.id]?.name || "",
          property_address: ownerMap[u.id]?.property_address || u.address || u.unit_number,
        })));
      } else {
        setUnits(unitData);
      }
      setLoading(false); setTypesLoading(false);
    })();
  }, [selectedAssoc]);

  const violsByUnit = useMemo(() => {
    const m: Record<string, any[]> = {};
    units.forEach(u => { m[u.id] = []; });
    violations.forEach(v => {
      // Match by unit_id or property_id first
      if (v.unit_id && m[v.unit_id]) { m[v.unit_id].push(v); return; }
      if (v.property_id && m[v.property_id]) { m[v.property_id].push(v); return; }
      // Fallback: match by normalized address
      if (v.address) {
        const vAddr = v.address.trim().toLowerCase();
        const matched = units.find(u => {
          const uAddr = (u.address || u.property_address || "").trim().toLowerCase();
          return uAddr && uAddr === vAddr;
        });
        if (matched) { m[matched.id].push(v); }
      }
    });
    return m;
  }, [units, violations]);

  const openViolations = useMemo(() => violations.filter(v => v.status === "open" || v.status === "Open" || v.status === "recommended_for_fining"), [violations]);
  const selectedAssocGeocodeHint = useMemo(() => formatAssociationGeocodeHint(selectedAssoc), [selectedAssoc]);
  const selectedAssocCenterAddress = useMemo(() => [selectedAssoc?.address, selectedAssocGeocodeHint].filter(Boolean).join(", "), [selectedAssoc?.address, selectedAssocGeocodeHint]);

  const filteredUnits = useMemo(() => {
    let list = units;
    if (searchQ.trim()) { const q = searchQ.toLowerCase(); list = list.filter(u => (u.address || "").toLowerCase().includes(q) || (u.owner_name || "").toLowerCase().includes(q) || (u.unit_number || "").toLowerCase().includes(q)); }
    if (filter === "open") list = list.filter(u => (violsByUnit[u.id] || []).some(v => v.status === "open" || v.status === "Open" || v.status === "recommended_for_fining"));
    if (filter === "clean") list = list.filter(u => !(violsByUnit[u.id]?.length));
    return list;
  }, [units, searchQ, filter, violsByUnit]);

  const existingForPopup = useMemo(() => {
    if (!popupAddress) return [];
    const addr = popupAddress.trim().toLowerCase();
    return violations.filter(v => (v.status === "open" || v.status === "recommended_for_fining") && (v.address || "").trim().toLowerCase() === addr);
  }, [popupAddress, violations]);
  const existingForInspect = useMemo(() => {
    if (!inspectUnit) return [];
    const unitViols = violsByUnit[inspectUnit.id] || [];
    return unitViols.filter((v: any) => v.status === "open" || v.status === "Open" || v.status === "recommended_for_fining");
  }, [inspectUnit, violsByUnit]);

  const handleSaved = async () => { await refreshViolations(); setPopupAddress(null); };
  const handleInspectSaved = async () => { await refreshViolations(); };

  const toggleUnitSelection = (id: string, checked: boolean | string) => {
    setSelectedUnitIds(prev => checked ? [...prev, id] : prev.filter(x => x !== id));
  };

  const handleSelectAllUnits = () => {
    if (selectedUnitIds.length === filteredUnits.length && filteredUnits.length > 0) setSelectedUnitIds([]);
    else setSelectedUnitIds(filteredUnits.map(u => u.id));
  };

  const handleBulkAssign = async () => {
    if (!bulkViolations.length || !selectedUnitIds.length) return;
    if (!window.confirm(`Assign ${bulkViolations.length} violation(s) to ${selectedUnitIds.length} properties? Existing matching violations will be escalated.`)) return;
    setIsBulkAssigning(true);
    try {
      const toInsert: any[] = [];
      const toEscalate: { id: string; currentStage: string | null }[] = [];
      const dateStr = new Date().toISOString().split("T")[0];
      selectedUnitIds.forEach(uid => {
        const u = units.find((x: any) => x.id === uid);
        if (!u) return;
        const unitViols = violsByUnit[uid] || [];
        bulkViolations.forEach(vid => {
          const vt = violationTypes.find((v: any) => v.id === vid);
          const category = vt?.category || vid;
          const match = unitViols.find((ex: any) =>
            (ex.category === category || ex.violation_type === category) &&
            (ex.status === "open" || ex.status === "Open" || ex.status === "recommended_for_fining")
          );
          if (match) {
            toEscalate.push({ id: match.id, currentStage: match.stage || match.notice_level || null });
          } else {
            toInsert.push({ association_id: selectedAssoc.id, property_id: u.id, address: u.address, violation_type: category, title: `${category} - ${u.address}`, category: vt?.category, status: "open", stage: "First Notice", notice_level: "First Notice", notes: bulkNotes || null, violation_date: dateStr });
          }
        });
      });
      if (toInsert.length > 0) {
        const { data: inserted, error } = await supabase.from("violations").insert(toInsert).select("id, address, violation_type, stage");
        if (error) throw error;
        for (const ins of inserted || []) {
          logViolationCreated(ins.id, { address: ins.address, violationType: ins.violation_type, noticeLevel: ins.stage });
        }
      }
      for (const esc of toEscalate) {
        const nextStage = getNextStage(esc.currentStage);
        if (nextStage) {
          await supabase.from("violations").update({ stage: nextStage, notice_level: nextStage }).eq("id", esc.id);
          await logStageChange(esc.id, esc.currentStage, nextStage);
        }
      }
      toast({ title: "Bulk Violations Assigned", description: `${toInsert.length} new, ${toEscalate.length} escalated across ${selectedUnitIds.length} properties.` });
      setSelectedUnitIds([]); setBulkViolations([]); setBulkNotes("");
      await refreshViolations();
    } catch (err: any) { toast({ variant: "destructive", title: "Error", description: err.message }); }
    finally { setIsBulkAssigning(false); }
  };

  const toggleBulkType = (id: string) => { setBulkViolations(prev => prev.includes(id) ? prev.filter(x => x !== id) : [...prev, id]); };

  const handleGenerateNotice = () => {
    if (!selectedUnit) return;
    try {
      const doc = new jsPDF();
      doc.setFontSize(18); doc.text("Violation Notice", 20, 20);
      doc.setFontSize(12);
      doc.text(`Date: ${new Date().toLocaleDateString()}`, 20, 35);
      doc.text(`Property: ${selectedUnit.address || selectedUnit.unit_number}`, 20, 45);
      doc.text(`Owner: ${selectedUnit.owner_name || "Current Resident"}`, 20, 55);
      doc.text("Notice Details:", 20, 75);
      doc.setFontSize(10);
      const splitText = doc.splitTextToSize(noticeText || "Please rectify the violation noted on your property.", 170);
      doc.text(splitText, 20, 85);
      doc.save(`Notice_${(selectedUnit.address || selectedUnit.unit_number).replace(/[^a-zA-Z0-9]/g, "_")}.pdf`);
      setShowNoticeModal(false); setNoticeText("");
      toast({ title: "Notice Generated", description: "PDF downloaded." });
    } catch (err: any) { toast({ variant: "destructive", title: "PDF Error", description: err.message }); }
  };

  return (
    <div style={{ fontFamily: "'Inter','Segoe UI',system-ui,sans-serif", background: B.bg, height: "calc(100vh - 64px)", color: B.darkGray, display: "flex", flexDirection: "column", overflow: "hidden", marginTop: -24, marginLeft: -24, marginRight: -24, marginBottom: -24 }}>
      <style>{`@keyframes spin{to{transform:rotate(360deg)}} @keyframes ping{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.4;transform:scale(1.5)}} input:focus,select:focus,textarea:focus{border-color:${B.blue}!important;box-shadow:0 0 0 3px rgba(0,82,155,.12)}`}</style>

      {/* Mobile association picker — replaces the old List/Map tab bar.
          On mobile we always show the map for pin-based violation logging. */}
      {isMobile && (
        <div style={{ display: "flex", alignItems: "center", gap: 8, padding: "8px 10px", background: B.white, borderBottom: `1px solid ${B.border}`, flexShrink: 0, zIndex: 20 }}>
          <MapIcon size={16} color={B.blue} style={{ flexShrink: 0 }} />
          <select
            value={selectedAssoc?.id || ""}
            onChange={e => { const c = associations.find(x => x.id === e.target.value) || null; setSelectedAssoc(c); setSelectedUnit(null); setSearchQ(""); }}
            style={{ ...inputStyle, height: 36, flex: 1 }}
          >
            <option value="">— Select an association —</option>
            {associations.map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
          </select>
        </div>
      )}

      <div style={{ display: "flex", flex: 1, overflow: "hidden" }}>
        {/* Sidebar — hidden entirely on mobile (mobile uses pin-tap to log violations). */}
        <div style={{
          width: isMobile ? 0 : 340,
          display: isMobile ? "none" : "flex",
          background: B.white, borderRight: isMobile ? "none" : `1px solid ${B.border}`,
          flexDirection: "column", flexShrink: 0, overflow: "hidden",
          boxShadow: isMobile ? "none" : "4px 0 16px rgba(0,0,0,.03)", zIndex: 10, position: "relative",
        }}>
          {/* Association picker */}
          <div style={{ padding: "14px", borderBottom: `1px solid ${B.border}`, flexShrink: 0, background: B.white }}>
            <SectionLabel>Association</SectionLabel>
            <select value={selectedAssoc?.id || ""} onChange={e => { const c = associations.find(x => x.id === e.target.value) || null; setSelectedAssoc(c); setSelectedUnit(null); setSearchQ(""); }}
              style={{ ...inputStyle, height: 36 }}>
              <option value="">— Select an association —</option>
              {associations.map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
            </select>
          </div>

          {/* Search + filter */}
          {selectedAssoc && (
            <div className="p-3 border-b border-slate-200 bg-slate-50 shrink-0">
              <input value={searchQ} onChange={e => setSearchQ(e.target.value)} placeholder="Search address or owner…" style={{ ...inputStyle, marginBottom: 10, height: 34 }} />
              <div className="flex gap-1.5 mb-3">
                {([["all", "All"], ["open", "Flagged"], ["clean", "Clean"]] as const).map(([v, l]) => (
                  <button key={v} onClick={() => setFilter(v)}
                    className={`flex-1 py-1.5 text-[11px] font-semibold rounded-md border transition-colors ${filter === v ? "bg-blue-600 text-white border-blue-600" : "bg-white text-slate-500 border-slate-200 hover:bg-slate-100"}`}>
                    {l}
                  </button>
                ))}
              </div>
              <div className="flex items-center justify-between bg-white px-3 py-2 rounded-lg border border-slate-200 shadow-sm">
                <div className="text-xs font-bold text-blue-700">{selectedUnitIds.length} Selected</div>
                <Button variant="outline" size="sm" onClick={handleSelectAllUnits} className="h-7 text-xs font-bold text-slate-700 border-slate-300 hover:bg-slate-100">
                  {selectedUnitIds.length === filteredUnits.length && filteredUnits.length > 0 ? "Deselect All" : "Select All"}
                </Button>
              </div>
            </div>
          )}

          {/* Unit list */}
          <div className="flex-1 overflow-y-auto bg-slate-50">
            {loading && <div className="flex items-center justify-center p-8 gap-3"><Spinner /><span className="text-slate-500 text-sm">Loading…</span></div>}
            {!loading && !selectedAssoc && <div className="text-center text-slate-400 text-sm">Select an association to begin.</div>}
            {!loading && selectedAssoc && filteredUnits.length === 0 && <div className="text-center text-slate-400 text-sm">No properties found.</div>}
            {!loading && filteredUnits.map((u, i) => {
              const viols = violsByUnit[u.id] || [];
              const open = viols.filter((v: any) => v.status === "open" || v.status === "Open" || v.status === "recommended_for_fining").length;
              const hasPhotos = viols.some((v: any) => v.photo_url || (Array.isArray(v.photo_urls) && v.photo_urls.length > 0));
              const sel = selectedUnit?.id === u.id;
              const isChecked = selectedUnitIds.includes(u.id);

              return (
                <div key={u.id}
                  className={`p-3 border-b flex items-start gap-3 transition-colors ${sel ? "bg-blue-50 border-l-[3px] border-l-blue-600" : isChecked ? "bg-blue-50/50 border-l-[3px] border-l-blue-300" : open ? "bg-white border-l-[3px] border-l-red-500" : "bg-white border-l-[3px] border-l-transparent"} hover:bg-blue-50/40`}>
                  <div className="pt-1">
                    <Checkbox checked={isChecked} onCheckedChange={(checked) => toggleUnitSelection(u.id, !!checked)} className="data-[state=checked]:bg-blue-600 data-[state=checked]:border-blue-600" />
                  </div>
                  <div className="flex-1 min-w-0 cursor-pointer" onClick={() => { setSelectedUnit(u); setPopupAddress(u.address); }}>
                    <div className="flex items-center gap-2 mb-1">
                      <div className={`w-5 h-5 rounded-full flex items-center justify-center text-[9px] font-bold text-white shrink-0 ${open && hasPhotos ? "bg-amber-500" : open ? "bg-red-500" : "bg-blue-600"}`}>{i + 1}</div>
                      <div className="text-[13px] font-bold text-slate-800 flex-1 truncate">{u.address || u.unit_number}</div>
                      {hasPhotos && <Camera className="w-3.5 h-3.5 text-amber-500 shrink-0" />}
                      {open > 0 && <InspBadge count={open} color={B.red} />}
                    </div>
                    {u.owner_name && <div className="text-[11px] text-slate-500 pl-7 truncate">{u.owner_name}</div>}
                  </div>
                </div>
              );
            })}
          </div>

          {/* Selected unit actions */}
          {selectedUnit && (
            <div className="p-4 border-t border-slate-200 bg-white shrink-0 shadow-[0_-4px_10px_rgba(0,0,0,0.02)] z-20">
              <div className="text-xs font-bold text-slate-800 mb-3 truncate">{selectedUnit.address || selectedUnit.unit_number}</div>
              <div className="flex flex-col gap-2">
                <div className="flex gap-2">
                  <button onClick={() => setPopupAddress(selectedUnit.address)} className="flex-1 bg-blue-600 hover:bg-blue-700 text-white font-bold text-xs py-2 rounded-lg transition-colors">+ Log Violation</button>
                  <button onClick={() => setInspectUnit(selectedUnit)} title="Full-Screen Inspection" className="px-3 bg-slate-100 hover:bg-slate-200 text-blue-600 font-bold text-sm rounded-lg border border-slate-200 transition-colors">⛶</button>
                </div>
                {selectedUnitIds.length <= 1 && (
                  <Button variant="outline" className="w-full text-xs font-bold bg-white border-blue-200 text-blue-700 hover:bg-blue-50" onClick={() => setShowNoticeModal(true)}>
                    <FileText className="w-3.5 h-3.5 mr-2" />Generate Notice
                  </Button>
                )}
              </div>
            </div>
          )}

          {/* Bulk Assignment Panel */}
          {selectedUnitIds.length > 0 && (
            <div className="shrink-0 bg-white border-t-[3px] border-t-blue-600 shadow-[0_-8px_20px_rgba(0,0,0,0.12)] z-30 flex flex-col max-h-[50vh]">
              <div className="p-3 bg-blue-50 border-b border-blue-100 flex justify-between items-center shrink-0">
                <div className="font-bold text-blue-900 text-sm flex items-center gap-2"><CheckSquare className="w-4 h-4 text-blue-600" />Bulk Assignment</div>
                <span className="bg-white text-blue-700 border border-blue-200 font-bold text-xs px-2 py-0.5 rounded">{selectedUnitIds.length} Selected</span>
              </div>
              <div className="p-4 overflow-y-auto flex-1 space-y-4 bg-white">
                <div>
                  <Label className="text-[10px] font-bold text-slate-500 uppercase tracking-wider mb-2 block">Select Violations</Label>
                  {typesLoading ? (
                    <div className="text-xs text-slate-500 flex items-center gap-2 p-2 bg-slate-50 rounded border border-slate-100"><Spinner size={14} /> Loading...</div>
                  ) : (!violationTypes || violationTypes.length === 0) ? (
                    <div className="text-xs text-slate-500 italic p-2 bg-slate-50 rounded border border-slate-100">No violation types configured.</div>
                  ) : (
                    <div className="space-y-1.5">
                      {violationTypes.map((vt: any) => (
                        <div key={vt.id} className="flex items-center space-x-2 bg-slate-50 p-2 rounded-lg border border-slate-100 hover:border-slate-300 transition-colors cursor-pointer" onClick={() => toggleBulkType(vt.id)}>
                          <Checkbox checked={bulkViolations.includes(vt.id)} onCheckedChange={() => toggleBulkType(vt.id)} className="data-[state=checked]:bg-blue-600" onClick={(e) => e.stopPropagation()} />
                          <Label className="text-xs cursor-pointer flex-1 flex items-center gap-2 font-medium text-slate-700" onClick={(e) => e.stopPropagation()}>
                            <span>{getIconForCategory(vt.category)}</span> {vt.category || "Unnamed Type"}
                          </Label>
                        </div>
                      ))}
                    </div>
                  )}
                </div>
                <div>
                  <Label className="text-[10px] font-bold text-slate-500 uppercase tracking-wider mb-2 block">Shared Notes</Label>
                  <Textarea placeholder="Add common inspection notes..." value={bulkNotes} onChange={(e) => setBulkNotes(e.target.value)} className="min-h-[70px] text-xs bg-slate-50 border-slate-200 focus:bg-white" />
                </div>
              </div>
              <div className="p-3 bg-slate-50 border-t border-slate-200 shrink-0">
                <Button disabled={bulkViolations.length === 0 || isBulkAssigning || typesLoading} onClick={handleBulkAssign} className="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-5 shadow-md">
                  {isBulkAssigning ? <Spinner size={16} color="white" /> : `Assign to ${selectedUnitIds.length} Properties`}
                </Button>
              </div>
            </div>
          )}
        </div>

        {/* Map Area — always visible on mobile so users can tap pins to log violations. */}
        <div style={{
          flex: 1,
          display: "flex",
          flexDirection: "column", overflow: "hidden", position: "relative",
        }}>
          {!selectedAssoc ? (
            <div style={{ flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 12, background: B.bg, padding: 16, textAlign: "center" }}>
              <div style={{ fontSize: 48 }}>🏛️</div>
              <div style={{ fontSize: 16, fontWeight: 600, color: B.midGray }}>Select an Association</div>
              <div style={{ fontSize: 13, color: B.lightGray }}>{isMobile ? "Use the picker above to choose one." : "Choose from the sidebar to load properties."}</div>
            </div>
          ) : loading ? (
            <div style={{ flex: 1, display: "flex", alignItems: "center", justifyContent: "center", gap: 12, background: B.bg }}><Spinner size={28} /><span style={{ color: B.midGray, fontSize: 13 }}>Loading properties…</span></div>
          ) : (
            <InteractiveMap units={filteredUnits} violationsByUnit={violsByUnit} selectedUnit={selectedUnit} onSelectUnit={(u: any) => setSelectedUnit(u)} onDropPin={(addr: string) => setPopupAddress(addr)} onLogViolation={(addr: string) => setPopupAddress(addr)} onResolve={refreshViolations} onEscalate={refreshViolations} centerAddress={selectedAssocCenterAddress} associationId={selectedAssoc?.id} associationAddress={selectedAssoc?.address} geocodeHint={selectedAssocGeocodeHint} />
          )}
        </div>
      </div>

      {/* Modals */}
      {popupAddress && selectedAssoc && <ViolationPopup address={popupAddress} associationId={selectedAssoc.id} existing={existingForPopup} onClose={() => setPopupAddress(null)} onSaved={handleSaved} violationTypes={violationTypes} typesLoading={typesLoading} />}
      {inspectUnit && selectedAssoc && <FullScreenInspection unit={inspectUnit} associationId={selectedAssoc.id} existing={existingForInspect} onClose={() => setInspectUnit(null)} onSaved={handleInspectSaved} violationTypes={violationTypes} typesLoading={typesLoading} />}

      {/* Notice Modal */}
      {showNoticeModal && selectedUnit && (
        <div className="fixed inset-0 bg-slate-900/60 backdrop-blur-sm flex items-center justify-center z-[500] p-4">
          <div className="bg-white rounded-2xl shadow-2xl w-full max-w-lg overflow-hidden border border-slate-200 flex flex-col">
            <div className="bg-slate-900 p-4 text-white flex justify-between items-center">
              <div>
                <h3 className="text-lg font-bold">Generate Violation Notice</h3>
                <p className="text-xs text-slate-300">Quick notice for single property</p>
              </div>
              <Button variant="ghost" size="icon" className="text-slate-300 hover:text-white" onClick={() => setShowNoticeModal(false)}><X className="w-5 h-5" /></Button>
            </div>
            <div className="space-y-5 bg-slate-50/50">
              <div className="grid grid-cols-2 gap-4">
                <div className="bg-white p-3 rounded-lg border border-slate-200 shadow-sm">
                  <Label className="text-[10px] font-bold text-slate-500 uppercase tracking-wider mb-1 block">Property Address</Label>
                  <div className="font-semibold text-slate-900 text-sm truncate">{selectedUnit.address || selectedUnit.unit_number}</div>
                </div>
                <div className="bg-white p-3 rounded-lg border border-slate-200 shadow-sm">
                  <Label className="text-[10px] font-bold text-slate-500 uppercase tracking-wider mb-1 block">Owner Name</Label>
                  <div className="font-semibold text-slate-900 text-sm truncate">{selectedUnit.owner_name || "Current Resident"}</div>
                </div>
              </div>
              <div>
                <Label className="text-[10px] font-bold text-slate-500 uppercase tracking-wider mb-2 block">Notice Content</Label>
                <Textarea value={noticeText} onChange={e => setNoticeText(e.target.value)} className="min-h-[120px] bg-white text-sm" placeholder="Please rectify the violation noted on your property..." />
              </div>
            </div>
            <div className="p-4 border-t border-slate-200 bg-white flex justify-end gap-3 shrink-0">
              <Button variant="outline" className="font-bold" onClick={() => setShowNoticeModal(false)}>Cancel</Button>
              <Button onClick={handleGenerateNotice} className="bg-blue-600 hover:bg-blue-700 text-white font-bold shadow-md"><Download className="w-4 h-4 mr-2" /> Download PDF</Button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}
