<template>
  <div class="operational-gis-map">
    <div ref="container" class="operational-gis-canvas" />
    <div class="operational-gis-provider"><span></span>PostGIS / WGS84</div>
    <div class="operational-gis-legend">
      <span><i class="object"></i>巡检对象</span>
      <span v-if="layers.routes"><i class="route"></i>计划航线</span>
      <span v-if="layers.routes"><i class="track"></i>实际航迹</span>
      <span v-if="layers.alarms"><i class="alarm"></i>告警点</span>
      <span><i class="device"></i>无人机</span>
    </div>
  </div>
</template>

<script setup lang="ts">
import { nextTick, onBeforeUnmount, onMounted, ref, watch } from "vue";
import maplibregl, { GeoJSONSource, LngLatBounds, Map as MapLibreMap } from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
import type { Row } from "../../types/demo-run";

const props = withDefaults(defineProps<{
  featureCollection?: Row;
  selectedId?: string;
  layers?: { routes: boolean; rules: boolean; alarms: boolean };
}>(), {
  featureCollection: () => ({ type: "FeatureCollection", features: [] }),
  selectedId: "",
  layers: () => ({ routes: true, rules: true, alarms: true })
});
const emit = defineEmits<{ select: [properties: Row] }>();
const container = ref<HTMLElement | null>(null);
let map: MapLibreMap | null = null;
let loaded = false;

const emptyStyle: maplibregl.StyleSpecification = {
  version: 8,
  sources: {},
  layers: [{ id: "background", type: "background", paint: { "background-color": "#eef3f7" } }]
};

function visibleCollection() {
  const features = Array.isArray(props.featureCollection?.features) ? props.featureCollection.features : [];
  return {
    type: "FeatureCollection",
    features: features.filter((feature: Row) => {
      const type = feature.properties?.domain_type;
      if (type === "alarm") return props.layers.alarms;
      if (["route", "flight_track"].includes(type)) return props.layers.routes;
      return true;
    })
  };
}

function addLayers() {
  if (!map || map.getSource("railway-features")) return;
  map.addSource("railway-features", { type: "geojson", data: visibleCollection() as GeoJSON.FeatureCollection });
  map.addLayer({ id: "object-fill", type: "fill", source: "railway-features", filter: ["all", ["==", ["get", "domain_type"], "inspection_object"], ["==", ["geometry-type"], "Polygon"]], paint: { "fill-color": "#4c9c78", "fill-opacity": 0.16, "fill-outline-color": "#287257" } });
  map.addLayer({ id: "object-line", type: "line", source: "railway-features", filter: ["all", ["==", ["get", "domain_type"], "inspection_object"], ["==", ["geometry-type"], "LineString"]], paint: { "line-color": "#17365d", "line-width": 5, "line-opacity": 0.82 } });
  map.addLayer({ id: "route-line", type: "line", source: "railway-features", filter: ["==", ["get", "domain_type"], "route"], paint: { "line-color": "#2563eb", "line-width": 3, "line-dasharray": [2, 2] } });
  map.addLayer({ id: "track-line", type: "line", source: "railway-features", filter: ["==", ["get", "domain_type"], "flight_track"], paint: { "line-color": "#16a34a", "line-width": 4 } });
  map.addLayer({ id: "device-point", type: "circle", source: "railway-features", filter: ["==", ["get", "domain_type"], "uav_device"], paint: { "circle-radius": 7, "circle-color": "#0f766e", "circle-stroke-color": "#ffffff", "circle-stroke-width": 2 } });
  map.addLayer({ id: "alarm-point", type: "circle", source: "railway-features", filter: ["==", ["get", "domain_type"], "alarm"], paint: { "circle-radius": ["case", ["==", ["get", "severity"], "critical"], 9, ["==", ["get", "severity"], "high"], 8, 6], "circle-color": ["match", ["get", "severity"], "critical", "#dc2626", "high", "#f97316", "medium", "#2563eb", "#16a34a"], "circle-stroke-color": ["case", ["==", ["to-string", ["id"]], props.selectedId], "#111827", "#ffffff"], "circle-stroke-width": ["case", ["==", ["to-string", ["id"]], props.selectedId], 4, 2] } });
  map.on("click", "alarm-point", (event) => {
    const properties = event.features?.[0]?.properties as Row | undefined;
    if (properties) emit("select", properties);
  });
  map.on("mouseenter", "alarm-point", () => { if (map) map.getCanvas().style.cursor = "pointer"; });
  map.on("mouseleave", "alarm-point", () => { if (map) map.getCanvas().style.cursor = ""; });
}

function eachCoordinate(value: unknown, callback: (coordinate: [number, number]) => void) {
  if (!Array.isArray(value)) return;
  if (value.length >= 2 && typeof value[0] === "number" && typeof value[1] === "number") callback([value[0], value[1]]);
  else value.forEach((item) => eachCoordinate(item, callback));
}

function fitFeatures() {
  if (!map) return;
  const bounds = new LngLatBounds();
  visibleCollection().features.forEach((feature: Row) => eachCoordinate(feature.geometry?.coordinates, (coordinate) => bounds.extend(coordinate)));
  if (!bounds.isEmpty()) map.fitBounds(bounds, { padding: 52, maxZoom: 15, duration: 0 });
}

function updateData(fit = false) {
  if (!map || !loaded) return;
  const source = map.getSource("railway-features") as GeoJSONSource | undefined;
  source?.setData(visibleCollection() as GeoJSON.FeatureCollection);
  if (map.getLayer("alarm-point")) {
    map.setPaintProperty("alarm-point", "circle-stroke-color", ["case", ["==", ["to-string", ["id"]], props.selectedId], "#111827", "#ffffff"]);
    map.setPaintProperty("alarm-point", "circle-stroke-width", ["case", ["==", ["to-string", ["id"]], props.selectedId], 4, 2]);
  }
  if (fit) fitFeatures();
}

onMounted(async () => {
  await nextTick();
  if (!container.value) return;
  map = new maplibregl.Map({ container: container.value, style: emptyStyle, center: [116.12, 39.105], zoom: 12, attributionControl: false });
  map.addControl(new maplibregl.NavigationControl({ showCompass: false }), "top-right");
  map.on("load", () => { loaded = true; addLayers(); fitFeatures(); });
});
watch(() => props.featureCollection, () => updateData(true), { deep: true });
watch(() => props.layers, () => updateData(false), { deep: true });
watch(() => props.selectedId, () => updateData(false));
onBeforeUnmount(() => { map?.remove(); map = null; });
</script>
