/**
 * #1961 — unit tests for ConnectionPointsLayer geometry handling.
 *
 * NSPD engineering_structures (cat 36328) приходят как Polygon/MultiPolygon в
 * EPSG:3857 (в проде: 587 Polygon + 298 MultiPolygon, НОЛЬ Point), а карта —
 * в EPSG:4326. extractLatLon должен:
 *   1) считать центроид внешнего кольца Polygon/MultiPolygon (не только Point);
 *   2) репроецировать 3857→4326, иначе маркер улетает в океан;
 *   3) graceful-skip (null) на пустой/невалидной геометрии.
 *
 * Тестируем чистую функцию (jsdom не монтирует Leaflet <CircleMarker>), т.к.
 * именно тут жил баг «точки подключения не на карте».
 */
import {
  classifyStructure,
  extractLatLon,
  groupStructuresByCategory,
} from "../ConnectionPointsLayer";
import type { EngineeringStructure } from "@/types/nspd";

// Реальный объект из прода (ТП в квартале 66:41:0204028) — Polygon в EPSG:3857.
// Внешнее кольцо вокруг (≈6739509, ≈7732776) → центр ЕКБ ≈ lon 60.5, lat 56.87.
const MERCATOR_POLYGON = {
  type: "Polygon",
  coordinates: [
    [
      [6739509.5, 7732776.0],
      [6739491.9, 7732789.0],
      [6739480.0, 7732760.0],
      [6739509.5, 7732776.0],
    ],
  ],
};

const MERCATOR_MULTIPOLYGON = {
  type: "MultiPolygon",
  coordinates: [
    [
      [
        [6739615.5, 7733013.2],
        [6739615.5, 7733013.2],
        [6739600.0, 7733000.0],
        [6739615.5, 7733013.2],
      ],
    ],
  ],
};

// Уже-градусная Point-геометрия (4326) — на случай будущей смены контракта.
const DEGREE_POINT = { type: "Point", coordinates: [60.54, 56.87] };

describe("extractLatLon", () => {
  it("returns centroid of a Mercator (EPSG:3857) Polygon reprojected to 4326", () => {
    const ll = extractLatLon(MERCATOR_POLYGON);
    expect(ll).not.toBeNull();
    const [lat, lon] = ll!;
    // Должно попасть в ЕКБ: lat ≈ 56.87, lon ≈ 60.5 — НЕ в океан (0,0).
    expect(lat).toBeGreaterThan(56.8);
    expect(lat).toBeLessThan(56.95);
    expect(lon).toBeGreaterThan(60.4);
    expect(lon).toBeLessThan(60.7);
  });

  it("returns centroid of a Mercator MultiPolygon (first ring)", () => {
    const ll = extractLatLon(MERCATOR_MULTIPOLYGON);
    expect(ll).not.toBeNull();
    const [lat, lon] = ll!;
    expect(lat).toBeGreaterThan(56.8);
    expect(lat).toBeLessThan(56.95);
    expect(lon).toBeGreaterThan(60.4);
    expect(lon).toBeLessThan(60.7);
  });

  it("handles a degree Point (no reprojection, lon/lat → lat/lon swap)", () => {
    const ll = extractLatLon(DEGREE_POINT);
    expect(ll).toEqual([56.87, 60.54]);
  });

  it("handles a Mercator Point (reprojects to 4326)", () => {
    const ll = extractLatLon({
      type: "Point",
      coordinates: [6739509.5, 7732776.0],
    });
    expect(ll).not.toBeNull();
    const [lat, lon] = ll!;
    expect(lat).toBeGreaterThan(56.8);
    expect(lat).toBeLessThan(56.95);
    expect(lon).toBeGreaterThan(60.4);
    expect(lon).toBeLessThan(60.7);
  });

  it("returns null for missing / empty / invalid geometry (graceful skip)", () => {
    expect(extractLatLon({})).toBeNull();
    expect(extractLatLon({ type: "Point" })).toBeNull();
    expect(extractLatLon({ type: "Point", coordinates: [1] })).toBeNull();
    expect(extractLatLon({ type: "Polygon", coordinates: [] })).toBeNull();
    expect(extractLatLon({ type: "Polygon", coordinates: [[]] })).toBeNull();
    expect(extractLatLon({ type: "MultiPolygon", coordinates: [] })).toBeNull();
    expect(extractLatLon({ type: "LineString", coordinates: [] })).toBeNull();
    expect(extractLatLon({ type: "GeometryCollection" })).toBeNull();
  });
});

// ── classification / grouping (sanity — unchanged behavior) ─────────────────
// NB: source="" чтобы исключить keyword "36328" (входит в electricity-список и
// иначе перекрывает name/type при source="nspd_36328").

function struct(partial: Partial<EngineeringStructure>): EngineeringStructure {
  return {
    name: null,
    type: null,
    cad_num: null,
    distance_to_boundary_m: 0,
    geometry_geojson: MERCATOR_POLYGON,
    readable_address: null,
    raw_props: {},
    source: "",
    ...partial,
  };
}

describe("classifyStructure / groupStructuresByCategory", () => {
  it("classifies by keyword in name/type", () => {
    expect(classifyStructure(struct({ name: "ТП-123 подстанция" }))).toBe(
      "electricity",
    );
    expect(classifyStructure(struct({ type: "газопровод" }))).toBe("gas");
    expect(classifyStructure(struct({ name: "Водопроводный колодец" }))).toBe(
      "water",
    );
    expect(classifyStructure(struct({ name: "что-то иное" }))).toBe("other");
  });

  it("groups every structure into a category bucket (no drops)", () => {
    const grouped = groupStructuresByCategory([
      struct({ name: "подстанция" }),
      struct({ type: "газопровод" }),
    ]);
    expect(grouped.get("electricity")).toHaveLength(1);
    expect(grouped.get("gas")).toHaveLength(1);
    expect(grouped.get("water")).toEqual([]);
  });
});
