import type {
  FilterSpecification,
  GeoJSONSource,
  GeoJSONSourceSpecification,
  StyleSpecification
} from "maplibre-gl";
import type { FeatureCollection, Geometry, LineString, Point, Polygon } from "geojson";
import { SOURCE_LAYERS } from "./sources";
import { MAP_STYLE_TOKENS } from "./map-colors";
import { WORKBENCH_INTERACTION_BEFORE_ID } from "./layers";

export const MEASUREMENT_SOURCE_ID = "workbench-measurement";
export const MEASUREMENT_SELECTED_PIPES_LAYER_ID = "workbench-measurement-selected-pipes";

export type MeasurementFeatureCollection = FeatureCollection<Geometry, { kind: "vertex" | "line" | "polygon" }>;

export const emptyMeasurementFeatureCollection: MeasurementFeatureCollection = {
  type: "FeatureCollection",
  features: []
};

export const measurementSource = {
  type: "geojson",
  data: emptyMeasurementFeatureCollection
} satisfies GeoJSONSourceSpecification;

export const measurementLayers: StyleSpecification["layers"] = [
  {
    id: MEASUREMENT_SELECTED_PIPES_LAYER_ID,
    type: "line",
    source: "pipes",
    "source-layer": SOURCE_LAYERS.pipes,
    minzoom: 9,
    filter: ["in", ["get", "id"], ["literal", []]],
    paint: {
      "line-color": MAP_STYLE_TOKENS.state.selected,
      "line-width": ["interpolate", ["linear"], ["zoom"], 9, 4, 13, 7, 17, 11],
      "line-opacity": 0.68
    }
  },
  {
    id: "workbench-measurement-polygon-fill",
    type: "fill",
    source: MEASUREMENT_SOURCE_ID,
    filter: ["==", ["get", "kind"], "polygon"],
    paint: {
      "fill-color": MAP_STYLE_TOKENS.tool.geometry,
      "fill-opacity": 0.12
    }
  },
  {
    id: "workbench-measurement-line",
    type: "line",
    source: MEASUREMENT_SOURCE_ID,
    filter: ["any", ["==", ["get", "kind"], "line"], ["==", ["get", "kind"], "polygon"]],
    paint: {
      "line-color": MAP_STYLE_TOKENS.tool.geometry,
      "line-width": 2.5,
      "line-dasharray": [2, 1.5],
      "line-opacity": 0.9
    }
  },
  {
    id: "workbench-measurement-vertex",
    type: "circle",
    source: MEASUREMENT_SOURCE_ID,
    filter: ["==", ["get", "kind"], "vertex"],
    paint: {
      "circle-color": MAP_STYLE_TOKENS.state.selected,
      "circle-radius": 4,
      "circle-stroke-color": MAP_STYLE_TOKENS.canvas.casing,
      "circle-stroke-width": 1.5
    }
  }
];

export function ensureMeasurementLayers(map: {
  getSource: (id: string) => unknown;
  addSource: (id: string, source: GeoJSONSourceSpecification) => void;
  getLayer: (id: string) => unknown;
  addLayer: (layer: NonNullable<StyleSpecification["layers"]>[number], beforeId?: string) => void;
}) {
  if (!map.getSource(MEASUREMENT_SOURCE_ID)) {
    map.addSource(MEASUREMENT_SOURCE_ID, measurementSource);
  }

  measurementLayers.forEach((layer) => {
    if (!map.getLayer(layer.id)) {
      map.addLayer(layer, WORKBENCH_INTERACTION_BEFORE_ID);
    }
  });
}

export function setMeasurementSourceData(
  map: { getSource: (id: string) => unknown },
  data: MeasurementFeatureCollection
) {
  const source = map.getSource(MEASUREMENT_SOURCE_ID) as GeoJSONSource | undefined;
  source?.setData(data);
}

export function setSelectedMeasurementPipeIds(
  map: { getLayer: (id: string) => unknown; setFilter: (id: string, filter?: FilterSpecification) => void },
  pipeIds: string[]
) {
  if (!map.getLayer(MEASUREMENT_SELECTED_PIPES_LAYER_ID)) {
    return;
  }

  map.setFilter(MEASUREMENT_SELECTED_PIPES_LAYER_ID, ["in", ["get", "id"], ["literal", pipeIds]]);
}

export function createMeasurementFeatureCollection(
  coordinates: [number, number][],
  mode: "distance" | "area" | "segment"
): MeasurementFeatureCollection {
  const features: MeasurementFeatureCollection["features"] = coordinates.map((coordinate, index) => ({
    type: "Feature",
    id: `measurement-vertex-${index}`,
    properties: { kind: "vertex" },
    geometry: {
      type: "Point",
      coordinates: coordinate
    } satisfies Point
  }));

  if (coordinates.length >= 2) {
    features.push({
      type: "Feature",
      id: "measurement-line",
      properties: { kind: "line" },
      geometry: {
        type: "LineString",
        coordinates
      } satisfies LineString
    });
  }

  if (mode === "area" && coordinates.length >= 3) {
    features.push({
      type: "Feature",
      id: "measurement-polygon",
      properties: { kind: "polygon" },
      geometry: {
        type: "Polygon",
        coordinates: [[...coordinates, coordinates[0]]]
      } satisfies Polygon
    });
  }

  return {
    type: "FeatureCollection",
    features
  };
}
