"use client";

import { useEffect, useRef, useState } from "react";
import {
  MapContainer,
  TileLayer,
  GeoJSON,
  CircleMarker,
  Popup,
  useMapEvents,
} from "react-leaflet";
import type { Feature, FeatureCollection, Geometry, Position } from "geojson";
import "leaflet/dist/leaflet.css";

import type { ParcelAnalysis } from "@/types/site-finder";
import type {
  ConnectionPointsResponse,
  EngineeringStructure,
} from "@/types/nspd";
import type { CustomPoi } from "@/types/customPoi";
import {
  ConnectionPointsLayer,
  CP_ALL_CATEGORIES,
  groupStructuresByCategory,
  type CpCategory,
} from "@/components/site-finder/ConnectionPointsLayer";
import { CpLayerControlPanel } from "@/components/site-finder/CpLayerControlPanel";
import { CustomPoiLayer } from "@/components/site-finder/CustomPoiLayer";
import { CustomPoiToggleButton } from "@/components/site-finder/CustomPoiToggleButton";
import { CustomPoiAddModal } from "@/components/site-finder/CustomPoiAddModal";
import { CustomPoiEditModal } from "@/components/site-finder/CustomPoiEditModal";
import {
  useAddCustomPoi,
  useUpdateCustomPoi,
  useDeleteCustomPoi,
} from "@/hooks/useCustomPois";

// ---------------------------------------------------------------------------
// POI legend config (for the legend row below the map)
// ---------------------------------------------------------------------------

interface CategoryStyle {
  color: string;
  label: string;
  radius: number;
}

export const CATEGORY_STYLES: Record<string, CategoryStyle> = {
  school: { color: "#1d4ed8", label: "Школа", radius: 8 },
  kindergarten: { color: "#1d4ed8", label: "Детский сад", radius: 8 },
  pharmacy: { color: "#10b981", label: "Аптека", radius: 6 },
  hospital: { color: "#f59e0b", label: "Больница", radius: 8 },
  shop_mall: { color: "#a855f7", label: "ТЦ", radius: 8 },
  shop_supermarket: { color: "#a855f7", label: "Супермаркет", radius: 6 },
  shop_small: { color: "#c084fc", label: "Магазин", radius: 6 },
  park: { color: "#16a34a", label: "Парк", radius: 6 },
  tram_stop: { color: "#dc2626", label: "Трамвай", radius: 6 },
  bus_stop: { color: "#6b7280", label: "Автобус", radius: 6 },
  metro_stop: { color: "#1e40af", label: "Метро", radius: 8 },
};

// ---------------------------------------------------------------------------
// Geometry centroid (bounding-box center — sufficient for zoom-to fit)
// ---------------------------------------------------------------------------

function flattenCoords(geom: Geometry): Position[] {
  switch (geom.type) {
    case "Point":
      return [geom.coordinates];
    case "MultiPoint":
    case "LineString":
      return geom.coordinates as Position[];
    case "MultiLineString":
    case "Polygon":
      return (geom.coordinates as Position[][]).flat();
    case "MultiPolygon":
      return (geom.coordinates as Position[][][]).flat(2);
    case "GeometryCollection":
      return geom.geometries.flatMap(flattenCoords);
    default:
      return [];
  }
}

function geomCenter(geom: Geometry): [number, number] {
  const coords = flattenCoords(geom);
  if (!coords.length) return [56.838, 60.6];
  let minLat = Infinity,
    maxLat = -Infinity,
    minLon = Infinity,
    maxLon = -Infinity;
  for (const [lon, lat] of coords) {
    if (lat < minLat) minLat = lat;
    if (lat > maxLat) maxLat = lat;
    if (lon < minLon) minLon = lon;
    if (lon > maxLon) maxLon = lon;
  }
  return [(minLat + maxLat) / 2, (minLon + maxLon) / 2];
}

// ---------------------------------------------------------------------------
// Props + Component
// ---------------------------------------------------------------------------

interface Props {
  data: ParcelAnalysis;
  isochrones?: FeatureCollection;
  connectionPoints?: ConnectionPointsResponse;
  customPois?: CustomPoi[];
  parcelCad?: string;
}

// ---------------------------------------------------------------------------
// Map click handler sub-component (must be inside MapContainer)
// ---------------------------------------------------------------------------

interface MapClickHandlerProps {
  editMode: boolean;
  onMapClick: (lat: number, lon: number) => void;
}

function MapClickHandler({ editMode, onMapClick }: MapClickHandlerProps) {
  useMapEvents({
    click(e) {
      if (editMode) {
        onMapClick(e.latlng.lat, e.latlng.lng);
      }
    },
  });
  return null;
}

export function SiteMap({
  data,
  isochrones,
  connectionPoints,
  customPois,
  parcelCad,
}: Props) {
  // Fix Leaflet default icon paths broken by webpack bundler
  useEffect(() => {
    void import("leaflet").then((L) => {
      // @ts-expect-error — _getIconUrl is internal Leaflet property
      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",
      });
    });
  }, []);

  // Connection-points layer toggle state (all categories visible by default)
  const [visibleCategories, setVisibleCategories] = useState<Set<CpCategory>>(
    new Set(CP_ALL_CATEGORIES),
  );

  // Custom POI add mode state
  const [addMode, setAddMode] = useState(false);
  const [pendingCoords, setPendingCoords] = useState<{
    lat: number;
    lon: number;
  } | null>(null);
  const [editingPoi, setEditingPoi] = useState<CustomPoi | null>(null);

  // Mutations
  const addMutation = useAddCustomPoi(parcelCad);
  const updateMutation = useUpdateCustomPoi(parcelCad);
  const deleteMutation = useDeleteCustomPoi(parcelCad);

  // Keyboard ESC cancels add mode
  const addModeRef = useRef(addMode);
  addModeRef.current = addMode;
  useEffect(() => {
    function onKeyDown(e: KeyboardEvent) {
      if (e.key === "Escape" && addModeRef.current) {
        setAddMode(false);
        setPendingCoords(null);
      }
    }
    window.addEventListener("keydown", onKeyDown);
    return () => window.removeEventListener("keydown", onKeyDown);
  }, []);

  function toggleCategory(cat: CpCategory) {
    setVisibleCategories((prev) => {
      const next = new Set(prev);
      if (next.has(cat)) {
        next.delete(cat);
      } else {
        next.add(cat);
      }
      return next;
    });
  }

  function toggleAll() {
    setVisibleCategories((prev) =>
      prev.size === CP_ALL_CATEGORIES.length
        ? new Set()
        : new Set(CP_ALL_CATEGORIES),
    );
  }

  function handleMapClick(lat: number, lon: number) {
    setPendingCoords({ lat, lon });
    setAddMode(false); // toggle off click mode; modal opens
  }

  const center: [number, number] = data.geom_geojson
    ? geomCenter(data.geom_geojson)
    : [56.838, 60.6];

  // Build legend from categories present in score_breakdown
  const presentCategories = Object.keys(data.score_breakdown);

  // Pre-group structures for both the map layer and the control panel
  const cpGrouped = connectionPoints
    ? groupStructuresByCategory(connectionPoints.engineering_structures)
    : new Map<CpCategory, EngineeringStructure[]>();

  return (
    <div>
      {/* Map */}
      <div
        style={{
          border: "1px solid #e5e7eb",
          borderRadius: 12,
          overflow: "hidden",
          height: 420,
          cursor: addMode ? "crosshair" : "grab",
        }}
      >
        <MapContainer
          center={center}
          zoom={15}
          style={{ height: "100%", width: "100%" }}
          scrollWheelZoom
        >
          <MapClickHandler editMode={addMode} onMapClick={handleMapClick} />
          <TileLayer
            attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OSM</a>'
            url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
          />

          {/* Parcel / quarter polygon */}
          {data.geom_geojson && (
            <GeoJSON
              key={data.cad_num}
              data={data.geom_geojson}
              style={{
                color: "#1d4ed8",
                weight: 2.5,
                fillColor: "#3b82f6",
                fillOpacity: 0.2,
              }}
            />
          )}

          {/* Isochrones — render in reverse order (largest range at bottom).
              NB: react-leaflet GeoJSON кэширует слой, не реагирует на смену
              `data` prop. Включаем в key первую координату feature (она
              различается между mode=foot/cycle/car и временем) — это
              форсирует remount при пересчёте от ORS. */}
          {isochrones &&
            [...isochrones.features].reverse().map((feature, idx) => {
              const seconds =
                typeof feature?.properties?.value === "number"
                  ? (feature.properties.value as number)
                  : 0;
              const minutes = seconds / 60;
              const opacity = Math.max(0.2, 0.5 - minutes * 0.02);
              const color =
                minutes <= 10
                  ? "#16a34a"
                  : minutes <= 20
                    ? "#3b82f6"
                    : "#a855f7";
              // Hash из первой координаты — различает foot/cycle/car полигоны
              const firstCoord = (
                feature.geometry as { coordinates?: number[][][] }
              )?.coordinates?.[0]?.[0];
              const coordHash = firstCoord
                ? `${firstCoord[0].toFixed(4)},${firstCoord[1].toFixed(4)}`
                : `${idx}`;
              return (
                <GeoJSON
                  key={`iso-${seconds}-${coordHash}`}
                  data={feature as Feature}
                  style={{
                    color,
                    fillColor: color,
                    fillOpacity: opacity,
                    weight: 1,
                  }}
                />
              );
            })}

          {/* POI markers */}
          {Object.entries(data.score_breakdown).flatMap(([cat, pois]) => {
            const style = CATEGORY_STYLES[cat];
            if (!style) return [];
            return pois
              .filter((poi) => poi.lat !== 0 && poi.lon !== 0)
              .map((poi, idx) => (
                <CircleMarker
                  key={`${cat}-${idx}`}
                  center={[poi.lat, poi.lon]}
                  radius={style.radius}
                  pathOptions={{
                    color: style.color,
                    fillColor: style.color,
                    fillOpacity: 0.85,
                    weight: 1.5,
                  }}
                >
                  <Popup>
                    <div style={{ fontSize: 13, lineHeight: 1.5 }}>
                      <strong>{style.label}</strong>
                      <br />
                      {poi.name ?? <em>без названия</em>}
                      <br />
                      {poi.distance_m.toFixed(0)} м
                      {poi.last_edit && (
                        <>
                          <br />
                          <span style={{ color: "#6b7280", fontSize: 11 }}>
                            OSM: {poi.last_edit}
                          </span>
                        </>
                      )}
                    </div>
                  </Popup>
                </CircleMarker>
              ));
          })}

          {/* Connection points layer — rendered on top of POI markers */}
          {connectionPoints && (
            <ConnectionPointsLayer
              grouped={cpGrouped}
              visibleCategories={visibleCategories}
            />
          )}

          {/* Custom POI markers — topmost layer */}
          {customPois && customPois.length > 0 && (
            <CustomPoiLayer
              pois={customPois}
              onEdit={(poi) => setEditingPoi(poi)}
              onDelete={(id) => deleteMutation.mutate(id)}
            />
          )}
        </MapContainer>
      </div>

      {/* Custom POI add-mode toggle button */}
      <div
        style={{ marginTop: 8, display: "flex", gap: 10, alignItems: "center" }}
      >
        <CustomPoiToggleButton
          active={addMode}
          onClick={() => {
            setAddMode((v) => !v);
            setPendingCoords(null);
          }}
        />
        {addMode && (
          <span style={{ fontSize: 12, color: "#6b7280" }}>
            Кликните на карте для добавления точки
          </span>
        )}
        {customPois && customPois.length > 0 && (
          <span style={{ fontSize: 12, color: "#374151", marginLeft: "auto" }}>
            {customPois.length} польз.{" "}
            {customPois.length === 1
              ? "точка"
              : customPois.length < 5
                ? "точки"
                : "точек"}
          </span>
        )}
      </div>

      {/* Add modal */}
      {pendingCoords && (
        <CustomPoiAddModal
          lat={pendingCoords.lat}
          lon={pendingCoords.lon}
          parcelCad={parcelCad ?? null}
          isLoading={addMutation.isPending}
          onSubmit={(payload) => {
            addMutation.mutate(payload, {
              onSuccess: () => setPendingCoords(null),
            });
          }}
          onCancel={() => setPendingCoords(null)}
        />
      )}

      {/* Edit modal */}
      {editingPoi && (
        <CustomPoiEditModal
          poi={editingPoi}
          isLoading={updateMutation.isPending}
          onSubmit={(updateData) => {
            updateMutation.mutate(
              { id: editingPoi.id, data: updateData },
              { onSuccess: () => setEditingPoi(null) },
            );
          }}
          onCancel={() => setEditingPoi(null)}
        />
      )}

      {/* POI category legend */}
      {presentCategories.length > 0 && (
        <div
          style={{
            display: "flex",
            flexWrap: "wrap",
            gap: "6px 12px",
            padding: "10px 4px 0",
          }}
        >
          {presentCategories.map((cat) => {
            const s = CATEGORY_STYLES[cat];
            if (!s) return null;
            const count = data.score_breakdown[cat]?.length ?? 0;
            return (
              <div
                key={cat}
                style={{
                  display: "flex",
                  alignItems: "center",
                  gap: 5,
                  fontSize: 12,
                  color: "#374151",
                }}
              >
                <span
                  style={{
                    width: 10,
                    height: 10,
                    borderRadius: "50%",
                    background: s.color,
                    flexShrink: 0,
                  }}
                />
                {s.label} ({count})
              </div>
            );
          })}
        </div>
      )}

      {!data.geom_geojson && (
        <p
          style={{
            marginTop: 8,
            fontSize: 12,
            color: "#9ca3af",
            textAlign: "center",
          }}
        >
          Геометрия участка не найдена — на карте нет полигона
        </p>
      )}

      {/* Connection points layer control panel — below the map */}
      {connectionPoints && (
        <CpLayerControlPanel
          data={connectionPoints}
          grouped={cpGrouped}
          visibleCategories={visibleCategories}
          onToggleCategory={toggleCategory}
          onToggleAll={toggleAll}
        />
      )}
    </div>
  );
}
