"use client";

/**
 * UtilityInfrastructureLayer — #1746: инженерная инфраструктура из OSM
 * (osm_utility_infrastructure_ekb) поверх карты Site Finder.
 *
 * Данные — преимущественно ЛЭП (power=line → LineString ways, ~95% объектов)
 * и подстанции (substation ways), плюс относительно немного точечных узлов
 * (towers → Point nodes). Поэтому слой ОБЯЗАН рисовать все три типа геометрии:
 *   Point                    → <CircleMarker>   (как ConnectionPointsLayer)
 *   LineString/MultiLineString → <GeoJSON> polyline (основная масса)
 *   Polygon/MultiPolygon      → <GeoJSON> filled polygon (подстанции)
 *
 * geometry_geojson приходит уже распарсенным GeoJSON-объектом (ST_AsGeoJSON на
 * бэке), координаты в порядке [lon, lat] — для точечных CircleMarker меняем на
 * [lat, lon]. Зеркалит ConnectionPointsLayer (CircleMarker+Popup) и ZouitLayer
 * (<GeoJSON> + key-remount по геометрии).
 *
 * Цвета — token-hex (документированное исключение карты/чартов, ui-tokens.md):
 * цвет per infrastructure_kind, группировка/видимость управляются из SiteMap.
 */

import { CircleMarker, GeoJSON, LayerGroup, Popup } from "react-leaflet";
import type { Feature, Geometry } from "geojson";

import type { UtilityInfrastructureFeature } from "@/hooks/useUtilityInfrastructure";

// ---------------------------------------------------------------------------
// Kind classification + styles
// ---------------------------------------------------------------------------

export type UtilityKind =
  "power" | "water" | "gas" | "heat" | "communication" | "sewage" | "other";

export interface UtilityKindStyle {
  color: string;
  label: string;
  radius: number;
}

// token-hex (map/chart exception per ui-tokens.md). Совпадает с палитрой
// CP_CATEGORY_STYLES, чтобы один и тот же ресурс читался одним цветом на карте.
export const UTILITY_KIND_STYLES: Record<UtilityKind, UtilityKindStyle> = {
  power: { color: "#f59e0b", label: "Электросети", radius: 6 }, // amber
  water: { color: "#06b6d4", label: "Водоснабжение", radius: 6 }, // cyan
  gas: { color: "#3b82f6", label: "Газоснабжение", radius: 6 }, // blue
  heat: { color: "#ef4444", label: "Теплоснабжение", radius: 6 }, // red
  communication: { color: "#10b981", label: "Связь", radius: 5 }, // emerald
  sewage: { color: "#8b5cf6", label: "Канализация", radius: 5 }, // violet
  other: { color: "#6b7280", label: "Прочее", radius: 5 }, // gray
};

// Порядок отрисовки / легенды: power первым (доминирующий слой), other последним.
export const UTILITY_ALL_KINDS: UtilityKind[] = [
  "power",
  "water",
  "gas",
  "heat",
  "communication",
  "sewage",
  "other",
];

/**
 * Нормализует infrastructure_kind (строка из бэка) в известный UtilityKind.
 * Бэк отдаёт power/water/gas/heat/communication/sewage; неизвестное → "other"
 * (graceful — слой не падает на новом виде).
 */
export function classifyUtilityKind(
  raw: string | null | undefined,
): UtilityKind {
  switch (raw) {
    case "power":
    case "water":
    case "gas":
    case "heat":
    case "communication":
    case "sewage":
      return raw;
    default:
      return "other";
  }
}

// ---------------------------------------------------------------------------
// Human-readable OSM type labels (#1746 follow-up)
// ---------------------------------------------------------------------------
//
// Попап раньше показывал сырой OSM-тег («Тип: power=substation») — нечитаемо.
// Маппим source_tag → человеческую подпись. Покрывает реальный вокабуляр
// osm_utility_infrastructure_ekb (power=substation/tower/line/minor_line,
// man_made=water_tower/water_works/wastewater_plant; pipeline/tower уточняются
// по виду ресурса). Неизвестный тег → null (строку «Тип» не показываем — лучше,
// чем сырой код или дубль вида).

const OSM_TYPE_LABELS: Record<string, string> = {
  "power=substation": "Электроподстанция",
  "power=tower": "Опора ЛЭП",
  "power=line": "ЛЭП (высоковольтная линия)",
  "power=minor_line": "Распределительная ЛЭП",
  "power=pole": "Опора электросети",
  "power=portal": "Портал ЛЭП",
  "man_made=water_tower": "Водонапорная башня",
  "man_made=water_works": "Водопроводная станция (водозабор)",
  "man_made=wastewater_plant": "Очистные сооружения",
  "man_made=storage_tank": "Резервуар",
};

/**
 * Человеческая подпись типа OSM-объекта по сырому тегу + виду ресурса.
 * pipeline/tower контекстны (зависят от kind). Неизвестный тег → null.
 */
export function humanOsmType(
  rawKind: string | null | undefined,
  sourceTag: string | null | undefined,
): string | null {
  const tag = sourceTag?.trim() || null;
  if (!tag) return null;
  const kind = classifyUtilityKind(rawKind);

  if (tag.endsWith("=pipeline")) {
    if (kind === "gas") return "Газопровод";
    if (kind === "water") return "Водопровод";
    if (kind === "heat") return "Теплотрасса";
    if (kind === "sewage") return "Канализационный коллектор";
    return "Трубопровод";
  }
  if (tag === "man_made=tower") {
    return kind === "communication" ? "Вышка / мачта связи" : "Башня / вышка";
  }
  return OSM_TYPE_LABELS[tag] ?? null;
}

// ---------------------------------------------------------------------------
// Geometry parsing
// ---------------------------------------------------------------------------

const POINT_TYPES = new Set<Geometry["type"]>(["Point"]);
const LINE_POLY_TYPES = new Set<Geometry["type"]>([
  "LineString",
  "MultiLineString",
  "Polygon",
  "MultiPolygon",
]);

/**
 * geometry_geojson уже распарсен (объект). Принимаем object | строку (на случай
 * будущей смены контракта). Возвращает Geometry либо null для пустого/невалидного
 * ввода — фича тогда просто не рисуется (graceful).
 */
export function parseUtilityGeometry(raw: unknown): Geometry | null {
  if (raw === null || raw === undefined) return null;

  let parsed: unknown = raw;
  if (typeof raw === "string") {
    if (raw.length === 0) return null;
    try {
      parsed = JSON.parse(raw);
    } catch {
      return null;
    }
  }

  if (typeof parsed !== "object" || parsed === null) return null;
  const candidate = parsed as { type?: unknown; coordinates?: unknown };
  if (
    typeof candidate.type !== "string" ||
    !Array.isArray(candidate.coordinates)
  ) {
    return null;
  }
  const t = candidate.type as Geometry["type"];
  if (!POINT_TYPES.has(t) && !LINE_POLY_TYPES.has(t)) return null;
  return parsed as Geometry;
}

// Точечная геометрия → [lat, lon] для Leaflet CircleMarker (GeoJSON = [lon,lat]).
function pointLatLon(geom: Geometry): [number, number] | null {
  if (geom.type !== "Point") return null;
  const coords = geom.coordinates;
  if (!Array.isArray(coords) || coords.length < 2) return null;
  const [lon, lat] = coords;
  if (typeof lat !== "number" || typeof lon !== "number") return null;
  return [lat, lon];
}

// ---------------------------------------------------------------------------
// Grouping helper (exported so SiteMap / panel build per-kind counts)
// ---------------------------------------------------------------------------

export interface UtilityMappable {
  feature: UtilityInfrastructureFeature;
  geometry: Geometry;
  kind: UtilityKind;
}

/**
 * Из сырых features оставляет только рисуемые (валидная геометрия) и группирует
 * по infrastructure_kind. Счётчики панели берутся отсюда — отражают именно
 * рисуемые объекты, а не все features.
 */
export function groupUtilityByKind(
  features: UtilityInfrastructureFeature[],
): Map<UtilityKind, UtilityMappable[]> {
  const grouped = new Map<UtilityKind, UtilityMappable[]>();
  for (const kind of UTILITY_ALL_KINDS) grouped.set(kind, []);

  for (const feature of features) {
    const geometry = parseUtilityGeometry(feature.geometry_geojson);
    if (!geometry) continue;
    const kind = classifyUtilityKind(feature.infrastructure_kind);
    grouped.get(kind)!.push({ feature, geometry, kind });
  }
  return grouped;
}

// ---------------------------------------------------------------------------
// Popup (shared by point + line/polygon render paths)
// ---------------------------------------------------------------------------

/**
 * Попап объекта инж. инфраструктуры OSM. Показываем ТОЛЬКО имеющиеся атрибуты —
 * вид (UTILITY_KIND_STYLES.label) + человеческую подпись типа (humanOsmType по
 * source_tag, без сырого «power=substation»), название, характеристики
 * (#2111 — pre-humanized строка с бэка: напряжение/диаметр/материал/оператор,
 * напр. «110 кВ/10 кВ · МРСК Урала», только при наличии), расстояние до границы
 * участка, источник (OSM osm_type/osm_id).
 */
function UtilityPopup({
  feature,
  style,
}: {
  feature: UtilityInfrastructureFeature;
  style: UtilityKindStyle;
}) {
  // Человеческая подпись типа объекта (НЕ сырой тег «power=substation»).
  const detail = humanOsmType(feature.infrastructure_kind, feature.source_tag);

  return (
    <Popup>
      <div style={{ fontSize: 12, lineHeight: 1.55, minWidth: 180 }}>
        <div
          style={{
            fontWeight: 700,
            marginBottom: 4,
            display: "flex",
            alignItems: "center",
            gap: 5,
          }}
        >
          <span
            style={{
              display: "inline-block",
              width: 10,
              height: 10,
              borderRadius: "50%",
              background: style.color,
              flexShrink: 0,
            }}
          />
          {style.label}
        </div>
        <div style={{ color: "#6b7280", marginBottom: 4, fontSize: 11 }}>
          Инж. сеть (OSM)
        </div>
        {feature.name && (
          <div style={{ marginBottom: 2 }}>
            <strong>{feature.name}</strong>
          </div>
        )}
        {detail && (
          <div style={{ color: "#6b7280", marginBottom: 2 }}>Тип: {detail}</div>
        )}
        {feature.characteristics && (
          <div style={{ color: "#6b7280", marginBottom: 2 }}>
            Характеристики: {feature.characteristics}
          </div>
        )}
        <div style={{ marginTop: 4, color: "#374151" }}>
          До границы участка:{" "}
          <strong>{Math.round(feature.distance_m)} м</strong>
        </div>
        <div
          style={{
            marginTop: 4,
            fontSize: 11,
            color: "#9ca3af",
            borderTop: "1px solid #f3f4f6",
            paddingTop: 4,
          }}
        >
          Источник: OpenStreetMap ({feature.osm_type} {feature.osm_id})
        </div>
      </div>
    </Popup>
  );
}

// ---------------------------------------------------------------------------
// Map layer
// ---------------------------------------------------------------------------

interface LayerProps {
  grouped: Map<UtilityKind, UtilityMappable[]>;
  visibleKinds: Set<UtilityKind>;
}

export function UtilityInfrastructureLayer({
  grouped,
  visibleKinds,
}: LayerProps) {
  return (
    <>
      {UTILITY_ALL_KINDS.map((kind) => {
        if (!visibleKinds.has(kind)) return null;
        const items = grouped.get(kind) ?? [];
        if (items.length === 0) return null;
        const style = UTILITY_KIND_STYLES[kind];

        return (
          <LayerGroup key={kind}>
            {items.map(({ feature, geometry }, idx) => {
              // Point → CircleMarker (узлы: towers и т.п.)
              if (geometry.type === "Point") {
                const latLon = pointLatLon(geometry);
                if (!latLon) return null;
                return (
                  <CircleMarker
                    key={`util-${kind}-pt-${feature.osm_type}-${feature.osm_id}-${idx}`}
                    center={latLon}
                    radius={style.radius}
                    pathOptions={{
                      color: style.color,
                      fillColor: style.color,
                      fillOpacity: 0.9,
                      weight: 2,
                    }}
                  >
                    <UtilityPopup feature={feature} style={style} />
                  </CircleMarker>
                );
              }

              // LineString / MultiLineString / Polygon / MultiPolygon → <GeoJSON>.
              // Заливка применяется только к полигонам; для линий fillOpacity
              // игнорируется Leaflet, но weight даёт видимую ЛЭП-линию.
              const isPolygon =
                geometry.type === "Polygon" || geometry.type === "MultiPolygon";
              const geoFeature: Feature = {
                type: "Feature",
                geometry,
                properties: {},
              };
              return (
                <GeoJSON
                  // react-leaflet GeoJSON кэширует слой и не реагирует на смену
                  // data prop — geom в key форсирует remount при смене участка
                  // (тот же приём, что у ZouitLayer/изохрон).
                  key={`util-${kind}-geo-${feature.osm_type}-${feature.osm_id}-${idx}`}
                  data={geoFeature}
                  style={{
                    color: style.color,
                    weight: isPolygon ? 1.5 : 2.5,
                    fillColor: style.color,
                    fillOpacity: isPolygon ? 0.3 : 0,
                  }}
                >
                  <UtilityPopup feature={feature} style={style} />
                </GeoJSON>
              );
            })}
          </LayerGroup>
        );
      })}
    </>
  );
}
