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

const DEFAULT_CENTER = { lat: 20.2961, lng: 85.8245 };
const GEOFENCE_STYLE = {
  strokeColor: "#003d9b",
  strokeOpacity: 0.85,
  strokeWeight: 2,
  fillColor: "#003d9b",
  fillOpacity: 0.12
};

function getMapsConfig() {
  return window.CONSTRUCT_PRO_MAPS || {};
}

function getMapsApiKey() {
  const key = getMapsConfig().apiKey;
  if (!key || key === "YOUR_GOOGLE_MAPS_API_KEY") return null;
  return key;
}

function getMapsProviderPreference() {
  const pref = String(getMapsConfig().provider || "auto").toLowerCase();
  if (pref === "google" || pref === "leaflet" || pref === "auto") return pref;
  return "auto";
}

async function validateGoogleMapsKey(apiKey) {
  try {
    const url = new URL("https://maps.googleapis.com/maps/api/geocode/json");
    url.searchParams.set("address", "Bhubaneswar");
    url.searchParams.set("key", apiKey);
    const response = await fetch(url.toString());
    const data = await response.json();
    if (data.status === "OK" || data.status === "ZERO_RESULTS") return { ok: true };
    return { ok: false, reason: data.error_message || data.status || "Google Maps unavailable" };
  } catch (_) {
    return { ok: false, reason: "Could not reach Google Maps" };
  }
}

function parseCoord(value) {
  const num = Number(value);
  return Number.isFinite(num) ? num : null;
}

function loadStylesheet(href, id) {
  if (document.getElementById(id)) return;
  const link = document.createElement("link");
  link.id = id;
  link.rel = "stylesheet";
  link.href = href;
  document.head.appendChild(link);
}

function loadScript(src, id) {
  if (document.getElementById(id)) {
    return window.L ? Promise.resolve(window.L) : new Promise((resolve) => {
      const existing = document.getElementById(id);
      existing.addEventListener("load", () => resolve(window.L));
    });
  }
  return new Promise((resolve, reject) => {
    const script = document.createElement("script");
    script.id = id;
    script.src = src;
    script.async = true;
    script.onload = () => resolve(window.L);
    script.onerror = () => reject(new Error("Map library failed to load"));
    document.head.appendChild(script);
  });
}

function loadLeaflet() {
  if (window.L?.map) return Promise.resolve(window.L);
  if (window.__constructProLeafletLoad) return window.__constructProLeafletLoad;
  loadStylesheet("https://unpkg.com/leaflet@1.9.4/dist/leaflet.css", "leaflet-css");
  window.__constructProLeafletLoad = loadScript("https://unpkg.com/leaflet@1.9.4/dist/leaflet.js", "leaflet-js").then((L) => {
    delete L.Icon.Default.prototype._getIconUrl;
    L.Icon.Default.mergeOptions({
      iconRetinaUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png",
      iconUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png",
      shadowUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png"
    });
    return L;
  });
  return window.__constructProLeafletLoad;
}

function loadGoogleMaps(apiKey) {
  if (window.google?.maps?.Map) return Promise.resolve(window.google.maps);
  if (window.__constructProMapsLoad) return window.__constructProMapsLoad;
  window.__constructProMapsLoad = new Promise((resolve, reject) => {
    const callbackName = "__constructProMapsReady";
    window[callbackName] = () => {
      delete window[callbackName];
      resolve(window.google.maps);
    };
    const script = document.createElement("script");
    script.src = `https://maps.googleapis.com/maps/api/js?key=${encodeURIComponent(apiKey)}&libraries=places&callback=${callbackName}`;
    script.async = true;
    script.defer = true;
    script.onerror = () => {
      delete window.__constructProMapsLoad;
      reject(new Error("Google Maps failed to load"));
    };
    document.head.appendChild(script);
  });
  return window.__constructProMapsLoad;
}

function getSearchRegion() {
  const region = getMapsConfig().searchRegion || {};
  return {
    state: region.state || "Odisha",
    country: region.country || "in",
    viewbox: Array.isArray(region.viewbox) ? region.viewbox : [81.35, 22.65, 87.55, 17.75],
    aliases: Array.isArray(region.aliases) ? region.aliases : ["odisha", "orissa", "bhubaneswar", "cuttack", "khordha"]
  };
}

function normalizeRegionalQuery(rawQuery, locationHint = "", address = "") {
  let query = String(rawQuery || "").trim();
  if (!query) return query;

  const region = getSearchRegion();
  const lower = query.toLowerCase();
  const hasRegionalContext = [region.state, "odisha", "orissa", ...region.aliases]
    .some((token) => lower.includes(String(token).toLowerCase()));

  const hint = String(locationHint || address || "").trim();
  if (hint && !lower.includes(hint.split(",")[0].trim().toLowerCase().slice(0, 4))) {
    query = `${query}, ${hint}`;
  }

  const queryLower = query.toLowerCase();
  const hasStateAfterHint = [region.state, "odisha", "orissa"]
    .some((token) => queryLower.includes(String(token).toLowerCase()));

  if (!hasRegionalContext && !hasStateAfterHint) {
    query = `${query}, ${region.state}, India`;
  }

  return query;
}

function odishaResultScore(label, lat, lng) {
  const name = String(label || "").toLowerCase();
  const region = getSearchRegion();
  let score = 0;

  if (name.includes("odisha")) score += 100;
  if (name.includes("orissa")) score += 100;
  region.aliases.forEach((alias) => {
    if (name.includes(String(alias).toLowerCase())) score += 25;
  });

  const [west, north, east, south] = region.viewbox;
  if (Number.isFinite(lat) && Number.isFinite(lng)) {
    if (lng >= west && lng <= east && lat >= south && lat <= north) score += 60;
  }

  return score;
}

function getResultLat(hit) {
  if (hit?.lat != null) return Number(hit.lat);
  const loc = hit?.geometry?.location;
  if (!loc) return NaN;
  return typeof loc.lat === "function" ? loc.lat() : Number(loc.lat);
}

function getResultLng(hit) {
  if (hit?.lon != null) return Number(hit.lon);
  if (hit?.lng != null) return Number(hit.lng);
  const loc = hit?.geometry?.location;
  if (!loc) return NaN;
  return typeof loc.lng === "function" ? loc.lng() : Number(loc.lng);
}

function pickBestRegionalHit(results) {
  if (!Array.isArray(results) || !results.length) return null;

  const ranked = [...results].sort((a, b) => {
    const latA = getResultLat(a);
    const lngA = getResultLng(a);
    const latB = getResultLat(b);
    const lngB = getResultLng(b);
    const labelA = a.display_name || a.formatted_address || "";
    const labelB = b.display_name || b.formatted_address || "";
    return odishaResultScore(labelB, latB, lngB) - odishaResultScore(labelA, latA, lngA);
  });

  const hit = ranked[0];
  const lat = getResultLat(hit);
  const lng = getResultLng(hit);
  if (!Number.isFinite(lat) || !Number.isFinite(lng)) return null;

  return {
    lat,
    lng,
    label: hit.display_name || hit.formatted_address || ""
  };
}

function getRegionalBounds(maps) {
  const [west, north, east, south] = getSearchRegion().viewbox;
  return new maps.LatLngBounds(
    { lat: south, lng: west },
    { lat: north, lng: east }
  );
}

async function geocodeQuery(query, locationHint = "", address = "") {
  const text = normalizeRegionalQuery(query, locationHint, address);
  if (!text) return null;

  const region = getSearchRegion();
  const [west, north, east, south] = region.viewbox;
  const url = new URL("https://nominatim.openstreetmap.org/search");
  url.searchParams.set("q", text);
  url.searchParams.set("format", "json");
  url.searchParams.set("limit", "8");
  url.searchParams.set("countrycodes", region.country);
  url.searchParams.set("viewbox", `${west},${north},${east},${south}`);

  const response = await fetch(url.toString(), {
    headers: { Accept: "application/json" }
  });
  if (!response.ok) return null;
  const results = await response.json();
  return pickBestRegionalHit(results);
}

function SiteMapPicker({
  latitude,
  longitude,
  radiusMeters = 150,
  address = "",
  locationHint = "",
  onCoordinatesChange,
  onRadiusChange,
  onAddressChange
}) {
  const mapRef = useRef(null);
  const searchRef = useRef(null);
  const mapInstanceRef = useRef(null);
  const markerRef = useRef(null);
  const circleRef = useRef(null);
  const providerRef = useRef(null);
  const autocompleteRef = useRef(null);
  const geocoderRef = useRef(null);
  const ignoreExternalSyncRef = useRef(false);

  const googleKey = getMapsApiKey();
  const providerPreference = getMapsProviderPreference();
  const [status, setStatus] = useState("loading");
  const [error, setError] = useState("");

  const lat = parseCoord(latitude);
  const lng = parseCoord(longitude);
  const radius = Math.max(25, Number(radiusMeters) || 150);

  const emitCoordinates = useCallback((nextLat, nextLng) => {
    ignoreExternalSyncRef.current = true;
    onCoordinatesChange?.({
      latitude: Number(nextLat.toFixed(6)),
      longitude: Number(nextLng.toFixed(6))
    });
    window.setTimeout(() => {
      ignoreExternalSyncRef.current = false;
    }, 0);
  }, [onCoordinatesChange]);

  const setSitePosition = useCallback((nextLat, nextLng, { pan = true, zoom = 17 } = {}) => {
    const position = { lat: nextLat, lng: nextLng };
    emitCoordinates(nextLat, nextLng);

    if (providerRef.current === "google" && mapInstanceRef.current && window.google?.maps) {
      const maps = window.google.maps;
      if (pan) {
        mapInstanceRef.current.panTo(position);
        if (zoom) mapInstanceRef.current.setZoom(zoom);
      }
      if (markerRef.current) markerRef.current.setPosition(position);
      else {
        markerRef.current = new maps.Marker({
          map: mapInstanceRef.current,
          position,
          draggable: true,
          title: "Site center"
        });
        markerRef.current.addListener("dragend", () => {
          const pos = markerRef.current.getPosition();
          if (!pos) return;
          emitCoordinates(pos.lat(), pos.lng());
          if (circleRef.current) circleRef.current.setCenter(pos);
        });
      }
      if (circleRef.current) circleRef.current.setCenter(position);
      return;
    }

    if (providerRef.current === "leaflet" && mapInstanceRef.current && window.L) {
      const L = window.L;
      const latLng = L.latLng(nextLat, nextLng);
      if (pan) {
        mapInstanceRef.current.setView(latLng, zoom || mapInstanceRef.current.getZoom());
      }
      if (markerRef.current) markerRef.current.setLatLng(latLng);
      if (circleRef.current) circleRef.current.setLatLng(latLng);
    }
  }, [emitCoordinates]);

  const initGoogleMap = useCallback(async (maps, center, hasCoords) => {
    const map = new maps.Map(mapRef.current, {
      center,
      zoom: hasCoords ? 16 : 13,
      mapTypeControl: false,
      streetViewControl: false,
      fullscreenControl: true,
      gestureHandling: "greedy"
    });
    mapInstanceRef.current = map;
    geocoderRef.current = new maps.Geocoder();
    providerRef.current = "google";

    circleRef.current = new maps.Circle({
      map,
      center,
      radius,
      ...GEOFENCE_STYLE,
      clickable: false
    });

    markerRef.current = new maps.Marker({
      map,
      position: center,
      draggable: true,
      title: "Site center"
    });
    markerRef.current.addListener("dragend", () => {
      const pos = markerRef.current.getPosition();
      if (!pos) return;
      emitCoordinates(pos.lat(), pos.lng());
      circleRef.current?.setCenter(pos);
    });

    map.addListener("click", (event) => {
      const pos = event.latLng;
      if (!pos) return;
      setSitePosition(pos.lat(), pos.lng(), { pan: false });
      circleRef.current?.setCenter(pos);
    });

    if (searchRef.current && maps.places) {
      autocompleteRef.current = new maps.places.Autocomplete(searchRef.current, {
        fields: ["geometry", "formatted_address", "name"],
        types: ["geocode"],
        componentRestrictions: { country: getSearchRegion().country },
        bounds: getRegionalBounds(maps),
        strictBounds: false
      });
      autocompleteRef.current.addListener("place_changed", () => {
        const place = autocompleteRef.current.getPlace();
        const location = place.geometry?.location;
        if (!location) return;
        setSitePosition(location.lat(), location.lng());
        if (place.formatted_address) onAddressChange?.(place.formatted_address);
      });
    }

    window.setTimeout(() => {
      maps.event.trigger(map, "resize");
      map.setCenter(markerRef.current?.getPosition() || center);
    }, 120);
  }, [emitCoordinates, onAddressChange, radius, setSitePosition]);

  const destroyMap = useCallback(() => {
    if (providerRef.current === "leaflet" && mapInstanceRef.current?.remove) {
      mapInstanceRef.current.remove();
    }
    if (providerRef.current === "google" && mapInstanceRef.current) {
      mapRef.current.innerHTML = "";
    }
    mapInstanceRef.current = null;
    markerRef.current = null;
    circleRef.current = null;
    geocoderRef.current = null;
    autocompleteRef.current = null;
    providerRef.current = null;
  }, []);

  const initLeafletMap = useCallback(async (L, center, hasCoords) => {
    const map = L.map(mapRef.current, {
      center: [center.lat, center.lng],
      zoom: hasCoords ? 16 : 13,
      scrollWheelZoom: true
    });
    L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
      attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
    }).addTo(map);
    mapInstanceRef.current = map;
    providerRef.current = "leaflet";

    circleRef.current = L.circle([center.lat, center.lng], {
      radius,
      color: GEOFENCE_STYLE.strokeColor,
      weight: GEOFENCE_STYLE.strokeWeight,
      opacity: GEOFENCE_STYLE.strokeOpacity,
      fillColor: GEOFENCE_STYLE.fillColor,
      fillOpacity: GEOFENCE_STYLE.fillOpacity
    }).addTo(map);

    markerRef.current = L.marker([center.lat, center.lng], { draggable: true }).addTo(map);
    markerRef.current.on("dragend", () => {
      const pos = markerRef.current.getLatLng();
      emitCoordinates(pos.lat, pos.lng);
      circleRef.current?.setLatLng(pos);
    });

    map.on("click", (event) => {
      setSitePosition(event.latlng.lat, event.latlng.lng, { pan: false });
      circleRef.current?.setLatLng(event.latlng);
    });

    window.setTimeout(() => map.invalidateSize(), 120);
  }, [emitCoordinates, radius, setSitePosition]);

  useEffect(() => {
    if (!mapRef.current) return undefined;
    let cancelled = false;

    const center = lat != null && lng != null ? { lat, lng } : DEFAULT_CENTER;
    const hasCoords = lat != null && lng != null;

    const boot = async () => {
      try {
        let useGoogle = providerPreference === "google" || (providerPreference === "auto" && Boolean(googleKey));

        if (useGoogle && googleKey) {
          const validation = await validateGoogleMapsKey(googleKey);
          if (!validation.ok) useGoogle = false;
        } else if (providerPreference === "google" && !googleKey) {
          useGoogle = false;
        }

        if (useGoogle && googleKey) {
          const maps = await loadGoogleMaps(googleKey);
          if (cancelled || !mapRef.current) return;
          destroyMap();
          await initGoogleMap(maps, center, hasCoords);
        } else {
          const L = await loadLeaflet();
          if (cancelled || !mapRef.current) return;
          destroyMap();
          await initLeafletMap(L, center, hasCoords);
        }
        if (!cancelled) {
          setStatus("ready");
          setError("");
          if (!hasCoords) emitCoordinates(center.lat, center.lng);
        }
      } catch (err) {
        if (cancelled) return;
        try {
          const L = await loadLeaflet();
          if (!cancelled && mapRef.current) {
            destroyMap();
            await initLeafletMap(L, center, hasCoords);
            setStatus("ready");
            setError("");
            if (!hasCoords) emitCoordinates(center.lat, center.lng);
            return;
          }
        } catch (_) {
          /* fall through */
        }
        setStatus("error");
        setError(err?.message || "Could not load map");
      }
    };

    boot();

    return () => {
      cancelled = true;
      destroyMap();
    };
  }, [googleKey, providerPreference, initGoogleMap, initLeafletMap, emitCoordinates, destroyMap]);

  useEffect(() => {
    if (ignoreExternalSyncRef.current) return;
    if (lat == null || lng == null || !mapInstanceRef.current) return;
    setSitePosition(lat, lng, { pan: true, zoom: null });
  }, [lat, lng, setSitePosition]);

  useEffect(() => {
    if (!circleRef.current) return;
    if (providerRef.current === "google") circleRef.current.setRadius(radius);
    else if (providerRef.current === "leaflet") circleRef.current.setRadius(radius);
  }, [radius]);

  const runSearch = async (queryOverride) => {
    const rawQuery = String(queryOverride ?? searchRef.current?.value ?? "").trim();
    if (!rawQuery) {
      window.ConstructProData?.showToast?.("Enter an address or place to search.");
      return;
    }
    const query = normalizeRegionalQuery(rawQuery, locationHint, address);

    const finishSearch = (hit) => {
      if (!hit) {
        window.ConstructProData?.showToast?.("Could not find that location in Odisha.");
        return;
      }
      setSitePosition(hit.lat, hit.lng);
      if (hit.label) onAddressChange?.(hit.label);
    };

    if (providerRef.current === "google" && geocoderRef.current && window.google?.maps) {
      const maps = window.google.maps;
      geocoderRef.current.geocode({
        address: query,
        region: getSearchRegion().country,
        componentRestrictions: { country: getSearchRegion().country },
        bounds: getRegionalBounds(maps)
      }, async (results, geocodeStatus) => {
        if (geocodeStatus === "OK" && results?.length) {
          const hit = pickBestRegionalHit(results);
          finishSearch(hit);
          return;
        }
        const hit = await geocodeQuery(rawQuery, locationHint, address);
        finishSearch(hit);
      });
      return;
    }

    try {
      setStatus("loading");
      const hit = await geocodeQuery(rawQuery, locationHint, address);
      finishSearch(hit);
      setStatus("ready");
    } catch (_) {
      window.ConstructProData?.showToast?.("Search failed. Try again.");
      setStatus("ready");
    }
  };

  const centerOnAddress = () => {
    const query = String(address || "").trim();
    if (!query) {
      window.ConstructProData?.showToast?.("Enter a site address first.");
      return;
    }
    if (searchRef.current) searchRef.current.value = query;
    runSearch(query);
  };

  const useDeviceLocation = () => {
    if (!navigator.geolocation) {
      window.ConstructProData?.showToast?.("Geolocation is not supported in this browser.");
      return;
    }
    navigator.geolocation.getCurrentPosition(
      (pos) => setSitePosition(pos.coords.latitude, pos.coords.longitude),
      () => window.ConstructProData?.showToast?.("Could not read device location. Check browser permissions.")
    );
  };

  return (
    <section className="site-map-picker" aria-labelledby="site-map-picker-title">
      <header className="site-map-picker__header">
        <div className="site-map-picker__header-copy">
          <p className="site-map-picker__eyebrow">Worker check-in zone</p>
          <h3 id="site-map-picker-title" className="site-map-picker__title">Site map & geofence</h3>
          <p className="site-map-picker__subtitle">Pin the site center and set the radius where field teams can check in.</p>
        </div>
        <span className="site-map-picker__region-badge">
          <Icons.radar size={16} />
          Odisha priority
        </span>
      </header>

      <div className="site-map-picker__controls">
        <form
          className="site-map-picker__search-shell"
          onSubmit={(event) => {
            event.preventDefault();
            runSearch();
          }}
        >
          <Icons.search size={20} />
          <input
            ref={searchRef}
            type="search"
            className="site-map-picker__search"
            placeholder="Search place in Odisha…"
            defaultValue=""
            autoComplete="off"
            aria-label="Search site location"
          />
          <button type="submit" className="site-map-picker__search-submit">Search</button>
        </form>
        <div className="site-map-picker__quick-actions">
          <button type="button" className="site-map-picker__chip" onClick={centerOnAddress}>
            <Icons.pin size={18} />
            Site address
          </button>
          <button type="button" className="site-map-picker__chip" onClick={useDeviceLocation}>
            <Icons.myLocation size={18} />
            My location
          </button>
        </div>
      </div>

      <div className="site-map-picker__metrics">
        <label className="site-map-picker__metric">
          <span className="site-map-picker__metric-label">Latitude</span>
          <input
            type="number"
            step="any"
            className="site-map-picker__metric-input"
            value={latitude}
            placeholder="20.2961"
            onChange={(event) => onCoordinatesChange?.({
              latitude: event.target.value === "" ? null : Number(event.target.value)
            })}
          />
        </label>
        <label className="site-map-picker__metric">
          <span className="site-map-picker__metric-label">Longitude</span>
          <input
            type="number"
            step="any"
            className="site-map-picker__metric-input"
            value={longitude}
            placeholder="85.8245"
            onChange={(event) => onCoordinatesChange?.({
              longitude: event.target.value === "" ? null : Number(event.target.value)
            })}
          />
        </label>
        <label className="site-map-picker__metric site-map-picker__metric--accent">
          <span className="site-map-picker__metric-label">Geofence radius</span>
          <div className="site-map-picker__metric-inline">
            <input
              type="number"
              min="25"
              step="1"
              className="site-map-picker__metric-input"
              value={radiusMeters}
              onChange={(event) => onRadiusChange?.(event.target.value)}
            />
            <span className="site-map-picker__metric-unit">m</span>
          </div>
        </label>
      </div>

      <div className="site-map-picker__map-wrap">
        <div ref={mapRef} className="site-map-picker__map" role="application" aria-label="Site location map picker" />
        {status === "loading" ? (
          <div className="site-map-picker__overlay">
            <span className="site-map-picker__spinner" aria-hidden="true" />
            Loading map…
          </div>
        ) : null}
        {status === "error" ? (
          <div className="site-map-picker__overlay site-map-picker__overlay--error">{error}</div>
        ) : null}
      </div>

      <p className="site-map-picker__hint">
        Click the map or drag the pin to adjust. The highlighted circle is the {radius} m worker boundary.
      </p>
    </section>
  );
}

Object.assign(window, { SiteMapPicker, getConstructProMapsApiKey: getMapsApiKey });
