"use client";

/**
 * ConceptResultMap — renders one variant's building placement: the parcel
 * polygon (outline) + the generated buildings FeatureCollection (filled), over
 * an OSM base. Re-uses the react-leaflet + GeoJSON pattern from SiteMap.
 * Lazy-mounted (ssr:false) by the parent — Leaflet touches `window`.
 */

import { useEffect, useMemo } from "react";
import { MapContainer, TileLayer, GeoJSON, useMap } from "react-leaflet";
import type {
  MultiPolygon,
  Polygon,
  FeatureCollection,
  Position,
} from "geojson";
import L from "leaflet";

import "leaflet/dist/leaflet.css";

import { useNeighborBuildings } from "@/hooks/useNeighborBuildings";

// ── Bounds helper ─────────────────────────────────────────────────────────────

/**
 * Union LatLng bounds over the parcel geometry. Handles Polygon (rings =
 * Position[][]) AND MultiPolygon (parts = Position[][][], flattened to all rings)
 * so a multi-part parcel fits to its WHOLE extent, not just part 0.
 */
function geometryBounds(
  geom: Polygon | MultiPolygon,
): L.LatLngBoundsExpression | null {
  const rings: Position[][] =
    geom.type === "MultiPolygon" ? geom.coordinates.flat() : geom.coordinates;
  let minLat = Infinity;
  let maxLat = -Infinity;
  let minLon = Infinity;
  let maxLon = -Infinity;
  for (const ring of rings) {
    for (const [lon, lat] of ring) {
      if (lat < minLat) minLat = lat;
      if (lat > maxLat) maxLat = lat;
      if (lon < minLon) minLon = lon;
      if (lon > maxLon) maxLon = lon;
    }
  }
  if (!Number.isFinite(minLat)) return null;
  return [
    [minLat, minLon],
    [maxLat, maxLon],
  ];
}

interface FitBoundsProps {
  bounds: L.LatLngBoundsExpression | null;
}

// react-leaflet does not auto-fit to data; do it imperatively once mounted.
function FitBounds({ bounds }: FitBoundsProps) {
  const map = useMap();
  useEffect(() => {
    if (bounds) {
      map.fitBounds(bounds, { padding: [24, 24] });
    }
  }, [map, bounds]);
  return null;
}

// ── Component ─────────────────────────────────────────────────────────────────

interface Props {
  /** Parcel geometry — Polygon OR MultiPolygon (all parts drawn + fit to). */
  parcel: Polygon | MultiPolygon;
  buildings: FeatureCollection;
  /** Keying makes react-leaflet remount the buildings layer on variant switch. */
  variantKey: string;
  height?: number;
  /**
   * Кадастровый номер участка — для слоя соседних зданий (макет квартала, #2180).
   * Отсутствует на standalone /concept без кадастра → слой соседей не грузится.
   */
  cadNum?: string | null;
}

export function ConceptResultMap({
  parcel,
  buildings,
  variantKey,
  height = 360,
  cadNum,
}: Props) {
  // Memoize so FitBounds only re-fits when the parcel geometry actually
  // changes — recomputing inline returns a new array each render, which would
  // make the fitBounds effect fire on every re-render and discard manual zoom/pan.
  const bounds = useMemo(() => geometryBounds(parcel), [parcel]);
  const hasBuildings = buildings.features.length > 0;

  // Соседние здания (#2180): контекст квартала под слоем корпусов. enabled только
  // при наличии cadNum (standalone /concept без кадастра → не грузим).
  const neighbors = useNeighborBuildings(cadNum);
  const neighborsData = neighbors.data;
  const hasNeighbors = (neighborsData?.count ?? 0) > 0;

  return (
    <div
      style={{
        border: "1px solid var(--border-card)",
        borderRadius: 12,
        overflow: "hidden",
        height,
        position: "relative",
      }}
    >
      <MapContainer
        center={[56.8389, 60.6057]}
        zoom={15}
        style={{ width: "100%", height: "100%" }}
        scrollWheelZoom
      >
        <TileLayer
          attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OSM</a>'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        />

        {/* Neighbour context buildings (#2180) — neutral grey макет квартала под
            слоем корпусов. Не интерактивный (interactive: false), без popup.
            Рендерится ПЕРВЫМ → уходит под слои участка и корпусов. Цвета: fill
            #E5E7EB, обводка #9CA3AF (нейтральные серые, ≈--fg-tertiary). */}
        {hasNeighbors && neighborsData && (
          <GeoJSON
            key="neighbors"
            data={neighborsData}
            interactive={false}
            style={{
              color: "#9CA3AF", // нейтральный серый — обводка соседей
              weight: 1,
              fillColor: "#E5E7EB", // нейтральный серый — заливка соседей
              fillOpacity: 0.6,
            }}
          />
        )}

        {/* Parcel boundary — accent-soft fill + accent outline (#2179) so the
            lot reads at a glance; buildings (opacity 0.55) still read on top of
            the lighter 0.35 parcel fill. Colours mirror --accent / --accent-soft. */}
        <GeoJSON
          key={`parcel-${variantKey}`}
          data={parcel}
          style={{
            color: "#1D4ED8", // --accent
            weight: 2,
            fillColor: "#DBEAFE", // --accent-soft
            fillOpacity: 0.35,
          }}
        />

        {/* Generated buildings — filled blocks. */}
        {hasBuildings && (
          <GeoJSON
            key={`buildings-${variantKey}`}
            data={buildings}
            style={{
              color: "#1d4ed8",
              weight: 1.5,
              fillColor: "#1d4ed8",
              fillOpacity: 0.55,
            }}
          />
        )}

        <FitBounds bounds={bounds} />
      </MapContainer>

      {!hasBuildings && (
        <div
          style={{
            position: "absolute",
            bottom: 12,
            left: 12,
            right: 12,
            zIndex: 1000,
            background: "var(--bg-card)",
            border: "1px solid var(--border-card)",
            borderRadius: 8,
            padding: "8px 12px",
            fontSize: 12,
            color: "var(--fg-secondary)",
          }}
        >
          Движок ещё не вернул геометрию зданий для этого варианта.
        </div>
      )}
    </div>
  );
}
