/**
 * Unit tests for the #1746 OSM utility-infrastructure layer pure logic: kind
 * classification, geometry_geojson parsing (the three-geometry-type contract +
 * graceful-skip), and grouping.
 *
 * The layer renders Leaflet <CircleMarker>/<GeoJSON> (jsdom can't mount those
 * meaningfully), so we test the data-shaping that decides WHAT gets drawn and
 * HOW it's colored — that's where the bugs live (power-lines must NOT be
 * skipped; [lon,lat]→[lat,lat] swap; bad parse → crash or ocean geometry).
 */
import type { UtilityInfrastructureFeature } from "@/hooks/useUtilityInfrastructure";
import {
  classifyUtilityKind,
  groupUtilityByKind,
  humanOsmType,
  parseUtilityGeometry,
} from "../UtilityInfrastructureLayer";

function feat(
  partial: Partial<UtilityInfrastructureFeature>,
): UtilityInfrastructureFeature {
  return {
    osm_id: 1,
    osm_type: "way",
    infrastructure_kind: "power",
    name: null,
    source_tag: null,
    distance_m: 0,
    geometry_geojson: {},
    ...partial,
  };
}

// geometry_geojson приходит уже распарсенным объектом (ST_AsGeoJSON на бэке).
const POINT_GEOM = { type: "Point", coordinates: [60.6, 56.83] };
const LINESTRING_GEOM = {
  type: "LineString",
  coordinates: [
    [60.6, 56.83],
    [60.61, 56.84],
  ],
};
const POLYGON_GEOM = {
  type: "Polygon",
  coordinates: [
    [
      [60.6, 56.83],
      [60.61, 56.83],
      [60.61, 56.84],
      [60.6, 56.83],
    ],
  ],
};

describe("classifyUtilityKind", () => {
  it("passes through known kinds", () => {
    expect(classifyUtilityKind("power")).toBe("power");
    expect(classifyUtilityKind("water")).toBe("water");
    expect(classifyUtilityKind("gas")).toBe("gas");
    expect(classifyUtilityKind("heat")).toBe("heat");
    expect(classifyUtilityKind("communication")).toBe("communication");
    expect(classifyUtilityKind("sewage")).toBe("sewage");
  });

  it("maps unknown / null kind to 'other' (graceful)", () => {
    expect(classifyUtilityKind("telecom")).toBe("other");
    expect(classifyUtilityKind(null)).toBe("other");
    expect(classifyUtilityKind(undefined)).toBe("other");
  });
});

describe("parseUtilityGeometry", () => {
  it("parses Point geometry (towers / nodes)", () => {
    expect(parseUtilityGeometry(POINT_GEOM)?.type).toBe("Point");
  });

  it("parses LineString geometry (power lines — the majority, must NOT skip)", () => {
    expect(parseUtilityGeometry(LINESTRING_GEOM)?.type).toBe("LineString");
  });

  it("parses Polygon geometry (substations)", () => {
    expect(parseUtilityGeometry(POLYGON_GEOM)?.type).toBe("Polygon");
  });

  it("parses MultiLineString / MultiPolygon geometry", () => {
    expect(
      parseUtilityGeometry({
        type: "MultiLineString",
        coordinates: [LINESTRING_GEOM.coordinates],
      })?.type,
    ).toBe("MultiLineString");
    expect(
      parseUtilityGeometry({
        type: "MultiPolygon",
        coordinates: [POLYGON_GEOM.coordinates],
      })?.type,
    ).toBe("MultiPolygon");
  });

  it("tolerates a GeoJSON string (future contract change)", () => {
    expect(parseUtilityGeometry(JSON.stringify(POINT_GEOM))?.type).toBe(
      "Point",
    );
  });

  it("returns null for missing / empty / invalid input (graceful skip)", () => {
    expect(parseUtilityGeometry(null)).toBeNull();
    expect(parseUtilityGeometry(undefined)).toBeNull();
    expect(parseUtilityGeometry("")).toBeNull();
    expect(parseUtilityGeometry("{not json")).toBeNull();
    expect(parseUtilityGeometry({})).toBeNull();
    expect(parseUtilityGeometry({ type: "Point" })).toBeNull();
    expect(
      parseUtilityGeometry({ type: "GeometryCollection", geometries: [] }),
    ).toBeNull();
  });
});

describe("groupUtilityByKind", () => {
  it("groups all three geometry types and keeps power lines", () => {
    const grouped = groupUtilityByKind([
      feat({ infrastructure_kind: "power", geometry_geojson: LINESTRING_GEOM }),
      feat({ infrastructure_kind: "power", geometry_geojson: POINT_GEOM }),
      feat({ infrastructure_kind: "power", geometry_geojson: POLYGON_GEOM }),
      feat({ infrastructure_kind: "water", geometry_geojson: POINT_GEOM }),
      feat({ infrastructure_kind: "gas", geometry_geojson: LINESTRING_GEOM }),
    ]);
    expect(grouped.get("power")).toHaveLength(3);
    expect(grouped.get("water")).toHaveLength(1);
    expect(grouped.get("gas")).toHaveLength(1);
  });

  it("drops features without valid geometry", () => {
    const grouped = groupUtilityByKind([
      feat({ infrastructure_kind: "power", geometry_geojson: LINESTRING_GEOM }),
      feat({ infrastructure_kind: "power", geometry_geojson: {} }),
    ]);
    expect(grouped.get("power")).toHaveLength(1);
  });

  it("buckets unknown kinds under 'other'", () => {
    const grouped = groupUtilityByKind([
      feat({ infrastructure_kind: "telecom", geometry_geojson: POINT_GEOM }),
    ]);
    expect(grouped.get("other")).toHaveLength(1);
  });

  it("returns empty buckets (not undefined) when features is empty", () => {
    const grouped = groupUtilityByKind([]);
    expect(grouped.get("power")).toEqual([]);
    expect(grouped.get("other")).toEqual([]);
  });
});

describe("humanOsmType", () => {
  it("maps known power tags to human labels", () => {
    expect(humanOsmType("power", "power=substation")).toBe("Электроподстанция");
    expect(humanOsmType("power", "power=tower")).toBe("Опора ЛЭП");
    expect(humanOsmType("power", "power=line")).toBe(
      "ЛЭП (высоковольтная линия)",
    );
  });

  it("resolves pipeline by resource kind", () => {
    expect(humanOsmType("gas", "man_made=pipeline")).toBe("Газопровод");
    expect(humanOsmType("water", "man_made=pipeline")).toBe("Водопровод");
    expect(humanOsmType("other", "man_made=pipeline")).toBe("Трубопровод");
  });

  it("resolves man_made=tower by kind (communication vs generic)", () => {
    expect(humanOsmType("communication", "man_made=tower")).toBe(
      "Вышка / мачта связи",
    );
    expect(humanOsmType("other", "man_made=tower")).toBe("Башня / вышка");
  });

  it("maps water man_made tags", () => {
    expect(humanOsmType("water", "man_made=water_tower")).toBe(
      "Водонапорная башня",
    );
    expect(humanOsmType("sewage", "man_made=wastewater_plant")).toBe(
      "Очистные сооружения",
    );
  });

  it("returns null for empty / unknown tags (no raw code shown)", () => {
    expect(humanOsmType("power", null)).toBeNull();
    expect(humanOsmType("power", "  ")).toBeNull();
    expect(humanOsmType("power", "power=unknown_thing")).toBeNull();
  });
});
