import "maplibre-gl/dist/maplibre-gl.css";

import maplibregl, { type Map as MapLibreMap, type MapSourceDataEvent } from "maplibre-gl";
import { useEffect, useRef, useState, type RefObject } from "react";
import { env } from "@/shared/config/env";
import type { DetailFeature } from "../types";
import {
  SIMULATION_SOURCE_IDS,
  simulationAnnotationLayers,
  simulationSources
} from "../map/annotation-layers";
import { fitNetworkBounds } from "../map/camera";
import {
  waterNetworkBusinessLayers,
  waterNetworkHitLayers,
  waterNetworkInteractionLayers
} from "../map/layers";
import { MAP_MAX_ZOOM } from "../map/map-layer-visuals";
import { createValueLabelLayers } from "../map/value-label";
import { setSimulationLayersVisibility } from "../map/simulation-layers";
import {
  AVAILABLE_WATER_NETWORK_SOURCE_IDS,
  createBaseStyle,
  createWaterNetworkSources,
  WATER_NETWORK_GLOBAL_VIEW
} from "../map/sources";
import {
  registerScadaImages,
  scadaBusinessLayers,
  scadaFallbackBusinessLayers,
  scadaHitLayers,
  scadaInteractionLayers
} from "../map/scada";
import { registerSupplyAssetImages } from "../map/supply-icons";
import { useMapInteractions } from "./use-map-interactions";
import { useSimulationLayerVisibility } from "./use-simulation-layer-visibility";

declare global {
  interface Window {
    __waterNetworkMap?: MapLibreMap;
  }
}

type UseWorkbenchMapOptions = {
  containerRef: RefObject<HTMLDivElement | null>;
  impactVisible: boolean;
  onSelectFeature: (feature: DetailFeature) => void;
  selectedFeature: DetailFeature | null;
};

type UseWorkbenchMapResult = {
  mapRef: RefObject<MapLibreMap | null>;
  mapReady: boolean;
  mapError: string | null;
  sourceStatuses: WorkbenchSourceStatus[];
  fitNetworkBounds: () => void;
};

export type WorkbenchSourceStatusValue = "loading" | "online" | "degraded" | "offline";

export type WorkbenchSourceStatus = {
  id: string;
  sourceName: string;
  status: WorkbenchSourceStatusValue;
  message: string;
};

const SOURCE_STATUS_LABELS: Record<string, string> = {
  "mapbox-base": "Mapbox 底图",
  "geoserver-mvt": "GeoServer MVT",
  "scada-icons": "SCADA 图标",
  "annotation-source": "业务标注源"
};

const SOURCE_GROUP_BY_ID: Record<string, string> = {
  "mapbox-light": "mapbox-base",
  "mapbox-satellite": "mapbox-base",
  ...Object.fromEntries(
    AVAILABLE_WATER_NETWORK_SOURCE_IDS.map((sourceId) => [sourceId, "geoserver-mvt"])
  ),
  [SIMULATION_SOURCE_IDS.impactArea]: "annotation-source",
  [SIMULATION_SOURCE_IDS.annotations]: "annotation-source"
};

export function useWorkbenchMap({
  containerRef,
  impactVisible,
  onSelectFeature,
  selectedFeature
}: UseWorkbenchMapOptions): UseWorkbenchMapResult {
  const mapRef = useRef<MapLibreMap | null>(null);
  const impactVisibleRef = useRef(impactVisible);
  const [mapReady, setMapReady] = useState(false);
  const [mapError, setMapError] = useState<string | null>(null);
  const [sourceStatuses, setSourceStatuses] = useState<Record<string, WorkbenchSourceStatus>>({});

  useEffect(() => {
    impactVisibleRef.current = impactVisible;
  }, [impactVisible]);

  useEffect(() => {
    if (!containerRef.current || mapRef.current) {
      return;
    }

    const mapboxToken = env.TJWATER_MAPBOX_ACCESS_TOKEN || undefined;
    const map = new maplibregl.Map({
      container: containerRef.current,
      style: createBaseStyle(mapboxToken),
      pitch: 0,
      bearing: 0,
      maxZoom: MAP_MAX_ZOOM,
      preserveDrawingBuffer: true,
      attributionControl: false
    });

    window.__waterNetworkMap = map;

    map.on("load", async () => {
      map.resize();
      const sources = createWaterNetworkSources();
      AVAILABLE_WATER_NETWORK_SOURCE_IDS.forEach((sourceId) =>
        map.addSource(sourceId, sources[sourceId])
      );
      map.addSource(SIMULATION_SOURCE_IDS.impactArea, simulationSources.impactArea);
      map.addSource(SIMULATION_SOURCE_IDS.annotations, simulationSources.annotations);
      await registerSupplyAssetImages(map);
      const scadaRegistration = await registerScadaImages(map);
      const activeScadaBusinessLayers =
        scadaRegistration.status === "unavailable"
          ? scadaFallbackBusinessLayers
          : scadaBusinessLayers;
      simulationAnnotationLayers
        .filter((layer) => layer.id === "simulation-impact-fill")
        .forEach((layer) => map.addLayer(layer));
      waterNetworkBusinessLayers.forEach((layer) => map.addLayer(layer));
      activeScadaBusinessLayers.forEach((layer) => map.addLayer(layer));
      simulationAnnotationLayers
        .filter((layer) => layer.type !== "symbol" && layer.id !== "simulation-impact-fill")
        .forEach((layer) => map.addLayer(layer));
      waterNetworkInteractionLayers.forEach((layer) => map.addLayer(layer));
      scadaInteractionLayers.forEach((layer) => map.addLayer(layer));
      createValueLabelLayers().forEach((layer) => map.addLayer(layer));
      simulationAnnotationLayers
        .filter((layer) => layer.type === "symbol")
        .forEach((layer) => map.addLayer(layer));
      waterNetworkHitLayers.forEach((layer) => map.addLayer(layer));
      scadaHitLayers.forEach((layer) => map.addLayer(layer));
      setSimulationLayersVisibility(map, impactVisibleRef.current);
      setMapReady(true);
      updateSourceStatus(
        setSourceStatuses,
        "scada-icons",
        scadaRegistration.status === "ready"
          ? "online"
          : scadaRegistration.status === "degraded"
            ? "degraded"
            : "offline",
        scadaRegistration.status === "ready"
          ? "SCADA 图标已就绪。"
          : scadaRegistration.status === "degraded"
            ? "部分 SCADA 图标加载失败，已使用默认图标。"
            : "SCADA 图标不可用，已使用圆点图层降级显示。"
      );
      updateSourceStatus(setSourceStatuses, "annotation-source", "online", "业务标注源已就绪。");
      fitNetworkBounds(map, WATER_NETWORK_GLOBAL_VIEW);
    });

    const resizeMap = () => map.resize();
    window.addEventListener("resize", resizeMap);
    window.setTimeout(resizeMap, 0);
    window.setTimeout(resizeMap, 300);
    window.setTimeout(resizeMap, 1000);

    map.on("sourcedata", (event) => {
      updateStatusFromSourceEvent(event, "online", setSourceStatuses);
    });

    map.on("sourcedataabort", (event) => {
      updateStatusFromSourceEvent(event, "degraded", setSourceStatuses);
    });

    map.on("error", (event) => {
      const message = event.error?.message ?? "地图渲染异常";
      if (!message.includes("AbortError")) {
        const sourceGroupId = getSourceGroupFromErrorEvent(event);
        if (sourceGroupId) {
          updateSourceStatus(
            setSourceStatuses,
            sourceGroupId,
            "offline",
            getSourceErrorMessage(sourceGroupId, message)
          );
        } else {
          setMapError(message);
        }
      }
    });

    mapRef.current = map;

    return () => {
      window.removeEventListener("resize", resizeMap);
      map.remove();
      mapRef.current = null;
      delete window.__waterNetworkMap;
    };
  }, [containerRef]);

  useMapInteractions({
    mapRef,
    mapReady,
    onSelectFeature,
    selectedFeature
  });

  useSimulationLayerVisibility({
    mapRef,
    mapReady,
    visible: impactVisible
  });

  function fitToNetworkBounds() {
    const map = mapRef.current;
    if (map) {
      fitNetworkBounds(map, WATER_NETWORK_GLOBAL_VIEW);
    }
  }

  return {
    mapRef,
    mapReady,
    mapError,
    sourceStatuses: Object.values(sourceStatuses),
    fitNetworkBounds: fitToNetworkBounds
  };
}

function updateStatusFromSourceEvent(
  event: MapSourceDataEvent,
  status: WorkbenchSourceStatusValue,
  setSourceStatuses: (
    updater: (
      current: Record<string, WorkbenchSourceStatus>
    ) => Record<string, WorkbenchSourceStatus>
  ) => void
) {
  const sourceGroupId = SOURCE_GROUP_BY_ID[event.sourceId];
  if (!sourceGroupId) {
    return;
  }

  if (status === "online" && !event.isSourceLoaded) {
    return;
  }

  updateSourceStatus(
    setSourceStatuses,
    sourceGroupId,
    status,
    getSourceStatusMessage(sourceGroupId, status)
  );
}

function updateSourceStatus(
  setSourceStatuses: (
    updater: (
      current: Record<string, WorkbenchSourceStatus>
    ) => Record<string, WorkbenchSourceStatus>
  ) => void,
  sourceGroupId: string,
  status: WorkbenchSourceStatusValue,
  message: string
) {
  setSourceStatuses((current) => {
    const previous = current[sourceGroupId];
    if (previous?.status === status && previous.message === message) {
      return current;
    }

    return {
      ...current,
      [sourceGroupId]: {
        id: sourceGroupId,
        sourceName: SOURCE_STATUS_LABELS[sourceGroupId] ?? sourceGroupId,
        status,
        message
      }
    };
  });
}

function getSourceGroupFromErrorEvent(event: { sourceId?: string; error?: { message?: string } }) {
  if (event.sourceId && SOURCE_GROUP_BY_ID[event.sourceId]) {
    return SOURCE_GROUP_BY_ID[event.sourceId];
  }

  const message = event.error?.message ?? "";
  if (message.includes("geoserver.waternetwork.cn")) {
    return "geoserver-mvt";
  }

  if (message.includes("api.mapbox.com") || message.includes("mapbox")) {
    return "mapbox-base";
  }

  return null;
}

function getSourceStatusMessage(sourceGroupId: string, status: WorkbenchSourceStatusValue) {
  if (sourceGroupId === "mapbox-base") {
    return status === "online"
      ? "底图瓦片已恢复正常。"
      : "底图瓦片请求中断，业务图层仍可继续使用。";
  }

  if (sourceGroupId === "geoserver-mvt") {
    return status === "online"
      ? "供水管网矢量瓦片已加载。"
      : "供水管网瓦片请求中断，当前视图可能不完整。";
  }

  if (sourceGroupId === "annotation-source") {
    return status === "online"
      ? "业务标注和影响范围已加载。"
      : "业务标注源加载中断，模拟标注可能暂不可见。";
  }

  return status === "online" ? "地图数据源已恢复正常。" : "地图数据源请求中断。";
}

function getSourceErrorMessage(sourceGroupId: string, errorMessage: string) {
  if (sourceGroupId === "mapbox-base") {
    return `底图瓦片请求失败：${errorMessage}`;
  }

  if (sourceGroupId === "geoserver-mvt") {
    return `GeoServer 矢量瓦片请求失败：${errorMessage}`;
  }

  if (sourceGroupId === "annotation-source") {
    return `业务标注源请求失败：${errorMessage}`;
  }

  return errorMessage;
}
