import type { Map as MapLibreMap, MapLayerMouseEvent } from "maplibre-gl";
import type { RefObject } from "react";
import { useEffect } from "react";
import type { DetailFeature } from "../types";
import {
  getFeatureId,
  getWaterNetworkSourceId,
  toDetailFeature
} from "../map/feature-adapter";
import { INTERACTIVE_HIT_LAYER_IDS } from "../map/layers";
import { SOURCE_LAYERS, type WaterNetworkSourceId } from "../map/sources";

export type MapFeatureInteractionState = {
  source: WaterNetworkSourceId;
  sourceLayer: string;
  id: string;
  hovered: boolean;
  selected: boolean;
};

type FeatureStateMap = Pick<MapLibreMap, "setFeatureState" | "removeFeatureState">;
type FeatureReference = Pick<MapFeatureInteractionState, "source" | "sourceLayer" | "id">;

export function toMapFeatureReference(
  feature: Pick<DetailFeature, "id" | "layer"> | null
): FeatureReference | null {
  if (!feature?.id) return null;
  return { source: feature.layer, sourceLayer: SOURCE_LAYERS[feature.layer], id: feature.id };
}

export function setMapFeatureInteractionState(
  map: FeatureStateMap,
  feature: FeatureReference,
  state: Partial<Pick<MapFeatureInteractionState, "hovered" | "selected">>
) {
  map.setFeatureState(
    { source: feature.source, sourceLayer: feature.sourceLayer, id: feature.id },
    state
  );
}

export function clearMapFeatureInteractionState(
  map: FeatureStateMap,
  feature: FeatureReference,
  key?: "hovered" | "selected"
) {
  map.removeFeatureState(
    { source: feature.source, sourceLayer: feature.sourceLayer, id: feature.id },
    key
  );
}

type UseMapInteractionsOptions = {
  mapRef: RefObject<MapLibreMap | null>;
  mapReady: boolean;
  onSelectFeature: (feature: DetailFeature) => void;
  selectedFeature: DetailFeature | null;
};

export function useMapInteractions({
  mapRef,
  mapReady,
  onSelectFeature,
  selectedFeature
}: UseMapInteractionsOptions) {
  useEffect(() => {
    const map = mapRef.current;
    if (!mapReady || !map) return;

    let hoveredFeature: FeatureReference | null = null;

    const clearHoveredFeature = () => {
      if (!hoveredFeature) return;
      clearMapFeatureInteractionState(map, hoveredFeature, "hovered");
      hoveredFeature = null;
    };

    const handleMouseMove = (event: MapLayerMouseEvent) => {
      const feature = event.features?.[0];
      const source = feature && getWaterNetworkSourceId(feature);
      const id = feature && getFeatureId(feature);
      if (!source || !id) return;

      const next = { source, sourceLayer: SOURCE_LAYERS[source], id };
      if (selectedFeature?.id === id && selectedFeature.layer === source) {
        clearHoveredFeature();
        return;
      }
      if (hoveredFeature?.id === id && hoveredFeature.source === source) return;

      clearHoveredFeature();
      setMapFeatureInteractionState(map, next, { hovered: true });
      hoveredFeature = next;
      map.getCanvas().style.cursor = "pointer";
    };

    const handleMouseLeave = () => {
      clearHoveredFeature();
      map.getCanvas().style.cursor = "";
    };

    const handleClick = (event: MapLayerMouseEvent) => {
      const feature = event.features?.[0];
      if (feature) onSelectFeature(toDetailFeature(feature));
    };

    const hitLayerIds = [...INTERACTIVE_HIT_LAYER_IDS];
    map.on("mousemove", hitLayerIds, handleMouseMove);
    map.on("mouseleave", hitLayerIds, handleMouseLeave);
    map.on("click", hitLayerIds, handleClick);

    return () => {
      clearHoveredFeature();
      map.off("mousemove", hitLayerIds, handleMouseMove);
      map.off("mouseleave", hitLayerIds, handleMouseLeave);
      map.off("click", hitLayerIds, handleClick);
    };
  }, [mapRef, mapReady, onSelectFeature, selectedFeature]);

  useEffect(() => {
    const map = mapRef.current;
    if (!mapReady || !map) return;
    const next = toMapFeatureReference(selectedFeature);
    if (next) setMapFeatureInteractionState(map, next, { selected: true, hovered: false });
    return () => {
      if (next) clearMapFeatureInteractionState(map, next, "selected");
    };
  }, [mapReady, mapRef, selectedFeature]);
}
