import { useCallback, useEffect, useRef, type Dispatch, type SetStateAction } from "react";
import Feature from "ol/Feature";
import { GeoJSON } from "ol/format";
import Point from "ol/geom/Point";
import { transform } from "ol/proj";
import { bbox, featureCollection } from "@turf/turf";

import { useChatToolActionHandler } from "@/hooks/useChatToolActionHandler";
import {
  applyJunctionAreaRender,
  type JunctionAreaRenderPayload,
} from "@components/olmap/DMALeakDetection/applyJunctionAreaRender";
import { apiFetch } from "@/lib/apiFetch";
import { queryFeaturesByIds } from "@/utils/mapQueryService";
import { config } from "@/config/config";
import { useMap } from "../MapComponent";
import type { DefaultLayerStyleId, StyleConfig } from "./styleEditorTypes";

type UseToolbarChatActionsParams = {
  setHighlightFeatures: Dispatch<SetStateAction<Feature[]>>;
  setChatPanelFeatureInfos: Dispatch<SetStateAction<[string, string][] | null>>;
  setChatPanelType: Dispatch<SetStateAction<"realtime" | "scheme" | "none">>;
  setChatPanelTimeRange: Dispatch<
    SetStateAction<{ startTime?: string; endTime?: string } | null>
  >;
  setShowHistoryPanel: Dispatch<SetStateAction<boolean>>;
  setShowStyleEditor: Dispatch<SetStateAction<boolean>>;
  setActiveTools: Dispatch<SetStateAction<string[]>>;
  applyExternalStyle: (
    layerId: DefaultLayerStyleId,
    styleConfig?: Partial<StyleConfig>
  ) => void;
  resetExternalStyle: (layerId: DefaultLayerStyleId) => void;
};

export const useToolbarChatActions = ({
  setHighlightFeatures,
  setChatPanelFeatureInfos,
  setChatPanelType,
  setChatPanelTimeRange,
  setShowHistoryPanel,
  setShowStyleEditor,
  setActiveTools,
  applyExternalStyle,
  resetExternalStyle,
}: UseToolbarChatActionsParams) => {
  const map = useMap();
  const chatJunctionRenderCleanupRef = useRef<(() => void) | null>(null);
  const renderRequestSeqRef = useRef(0);

  const disposeChatJunctionRender = useCallback(() => {
    chatJunctionRenderCleanupRef.current?.();
    chatJunctionRenderCleanupRef.current = null;
  }, []);

  useEffect(() => () => disposeChatJunctionRender(), [disposeChatJunctionRender]);

  useChatToolActionHandler(
    useCallback(
      (action) => {
        const geojsonFormat = new GeoJSON();
        const zoomToFeatures = (
          features: Feature[],
          geometryKind: "point" | "line",
        ) => {
          if (features.length === 0) return;

          if (geometryKind === "point" && features.length === 1) {
            const geometry = features[0].getGeometry();
            if (geometry instanceof Point) {
              map?.getView().animate({
                center: geometry.getCoordinates(),
                zoom: 18,
                duration: 1000,
              });
              return;
            }
          }

          const geojsonFeatures = features.map((feature) =>
            geojsonFormat.writeFeatureObject(feature),
          );
          const extent = bbox(featureCollection(geojsonFeatures as any));
          if (extent) {
            map?.getView().fit(extent, {
              maxZoom: 18,
              duration: 1000,
              padding:
                geometryKind === "line"
                  ? [60, 60, 60, 60]
                  : [40, 40, 40, 40],
            });
          }
        };

        const locateFeatures = (
          ids: string[],
          layer: string,
          geometryKind: "point" | "line",
        ) => {
          queryFeaturesByIds(ids, layer).then((features) => {
            if (features.length > 0) {
              setHighlightFeatures(features);
              zoomToFeatures(features, geometryKind);
            }
          });
        };

        switch (action.type) {
          case "locate_features": {
            locateFeatures(action.ids, action.layer, action.geometryKind);
            break;
          }
          case "zoom_to_map": {
            const center =
              action.sourceCrs === "EPSG:4326"
                ? transform(action.coordinate, "EPSG:4326", "EPSG:3857")
                : action.coordinate;
            map?.getView().animate({
              center,
              zoom: action.zoom ?? map.getView().getZoom() ?? 18,
              duration: action.durationMs ?? 1000,
            });
            break;
          }
          case "view_history": {
            setChatPanelFeatureInfos(action.featureInfos);
            setChatPanelType(action.dataType);
            setChatPanelTimeRange({
              startTime: action.startTime,
              endTime: action.endTime,
            });
            setShowHistoryPanel(true);
            break;
          }
          case "view_scada": {
            setChatPanelFeatureInfos(action.featureInfos);
            setChatPanelType("none");
            setChatPanelTimeRange({
              startTime: action.startTime,
              endTime: action.endTime,
            });
            setShowHistoryPanel(true);
            setActiveTools((prev) => {
              if (prev.includes("history")) {
                return prev;
              }
              return [...prev, "history"];
            });
            break;
          }
          case "render_junctions": {
            disposeChatJunctionRender();
            renderRequestSeqRef.current += 1;
            const requestSeq = renderRequestSeqRef.current;

            if (!action.renderRef || !map) {
              break;
            }

            void (async () => {
              try {
                const query = action.sessionId
                  ? `?session_id=${encodeURIComponent(action.sessionId)}`
                  : "";
                const response = await apiFetch(
                  `${config.AGENT_URL}/api/v1/agent/render-references/${encodeURIComponent(action.renderRef)}${query}`,
                  {
                    method: "GET",
                    projectHeaderMode: "include",
                    skipAuthRedirect: true,
                  },
                );

                if (!response.ok) {
                  throw new Error(`render ref request failed: ${response.status}`);
                }

                const payload = (await response.json()) as {
                  data?: {
                    node_area_map?: Record<string, unknown>;
                    area_ids?: unknown[];
                    area_colors?: Record<string, unknown>;
                  };
                };

                const data = payload.data;
                if (!data?.node_area_map) {
                  throw new Error("render ref payload missing node_area_map");
                }

                const renderPayload: JunctionAreaRenderPayload = {
                  nodeAreaMap: Object.fromEntries(
                    Object.entries(data.node_area_map).map(([key, value]) => [
                      String(key),
                      String(value ?? ""),
                    ]),
                  ),
                  areaIds: Array.isArray(data.area_ids)
                    ? data.area_ids.map((item) => String(item).trim()).filter(Boolean)
                    : [],
                  areaColors:
                    data.area_colors && typeof data.area_colors === "object"
                      ? Object.fromEntries(
                          Object.entries(data.area_colors).map(([key, value]) => [
                            String(key),
                            String(value ?? ""),
                          ]),
                        )
                      : {},
                };

                if (
                  requestSeq !== renderRequestSeqRef.current ||
                  Object.keys(renderPayload.nodeAreaMap).length === 0
                ) {
                  return;
                }

                chatJunctionRenderCleanupRef.current = applyJunctionAreaRender(
                  map,
                  renderPayload,
                  { propertyKey: "chat_junction_render_index" },
                );
              } catch (error) {
                console.error("Failed to resolve render_ref for junction render:", error);
              }
            })();
            break;
          }
          case "apply_layer_style": {
            setShowStyleEditor(true);
            setActiveTools((prev) => (prev.includes("style") ? prev : [...prev, "style"]));
            if (action.resetToDefault) {
              resetExternalStyle(action.layerId);
            } else {
              applyExternalStyle(action.layerId, action.styleConfig);
            }
            break;
          }
        }
      },
      [
        applyExternalStyle,
        disposeChatJunctionRender,
        map,
        resetExternalStyle,
        setActiveTools,
        setChatPanelFeatureInfos,
        setChatPanelTimeRange,
        setChatPanelType,
        setHighlightFeatures,
        setShowHistoryPanel,
        setShowStyleEditor,
      ],
    ),
  );
};
