"use client";

import type { Map as MapLibreMap } from "maplibre-gl";
import {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
  type ComponentProps,
  type CSSProperties,
  type KeyboardEvent as ReactKeyboardEvent,
  type PointerEvent as ReactPointerEvent
} from "react";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
import {
  AgentCollapsedRail,
  AgentCommandPanel,
  AgentPersona,
  type AgentUiResult
} from "@/features/agent";
import { toTrustedMapAction, type UIEnvelopePayload } from "@/features/agent/ui-envelope";
import type { FrontendActionRequest } from "@/features/agent/frontend-action";
import {
  MapErrorNotice,
  MapLoadingNotice,
  MapSourceStatusNotice,
  showMapNotice,
  type MapSourceStatus
} from "@/features/map/core/components/notice";
import { MapScaleLine } from "@/features/map/core/components/scale-line";
import { MapToolbar, type MapToolbarItem } from "@/features/map/core/components/toolbar";
import { MapZoom } from "@/features/map/core/components/zoom";
import {
  MAP_CONTROL_SURFACE_CLASS_NAME,
  MAP_MAJOR_PANEL_RADIUS_CLASS_NAME
} from "@/features/map/core/components/map-control-styles";
import { cn } from "@/lib/utils";
import { AgentTaskTicker } from "./components/agent-task-ticker";
import { MapDevPanel } from "./components/map-dev-panel";
import { FeaturePopover } from "./components/feature-popover";
import { ScheduledConditionFeed } from "./components/scheduled-condition-feed";
import { ToolbarPanel, type ExportViewPreset, type ToolbarPanelProps, type ToolbarToolId } from "./components/toolbar-panel";
import { WorkbenchTopBar } from "./components/workbench-top-bar";
import {
  SCHEDULED_CONDITION_REFRESH_INTERVAL_MS,
  createOperationalScheduledConditions
} from "./data/scheduled-conditions";
import { WORKBENCH_SCENARIOS, WORKBENCH_USER } from "./data/workbench-session";
import { useWorkbenchAgent } from "./hooks/use-workbench-agent";
import { useWorkbenchDrawing, type WorkbenchDrawMode } from "./hooks/use-workbench-drawing";
import { useWorkbenchMap } from "./hooks/use-workbench-map";
import { useWorkbenchMapController } from "./hooks/use-workbench-map-controller";
import { toMapFeatureReference } from "./hooks/use-map-interactions";
import { useWorkbenchMeasurement, type WorkbenchMeasureMode, type WorkbenchMeasureUnit } from "./hooks/use-workbench-measurement";
import {
  WORKBENCH_LAYOUT,
  clampAgentPanelWidth,
  getWorkbenchBasemapTone,
  getWorkbenchLayoutCssVariables,
  getWorkbenchViewportLayout
} from "./layout/workbench-layout";
import { getResponsiveWorkbenchPadding } from "./map/camera";
import { exportMapViewImage } from "./map/export-view";
import { parseScadaAnalysisItems } from "./map/scada-analysis";
import {
  BASE_LAYER_IDS,
  BASE_LAYER_OPTIONS,
  INITIAL_LAYER_VISIBILITY,
  MAP_LEGEND_ITEMS,
  WORKBENCH_LAYER_GROUPS,
  createLayerControlItems,
  getWorkbenchLayerIds
} from "./map/map-control-config";
import { waterNetworkToolbarItems } from "./map/toolbar-config";
import type { DetailFeature, ScheduledConditionItem, ScheduledConditionRecord, WorkbenchAlert } from "./types";
import { createAlertQueueConversationPrompt } from "./utils/scheduled-condition-prompts";

const HEADER_DATA_TIME_STEP_MINUTES = 5;
const INITIAL_HEADER_DATA_TIME = "同步中";
const WORKBENCH_LAYOUT_CSS_VARIABLES = getWorkbenchLayoutCssVariables();

function formatHeaderDataTime(date: Date) {
  const snappedMinutes = Math.floor(date.getMinutes() / HEADER_DATA_TIME_STEP_MINUTES) * HEADER_DATA_TIME_STEP_MINUTES;
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const day = String(date.getDate()).padStart(2, "0");
  const hours = String(date.getHours()).padStart(2, "0");
  const minutes = String(snappedMinutes).padStart(2, "0");

  return `${year}-${month}-${day} ${hours}:${minutes}`;
}

function getMsUntilNextHeaderDataTimeTick(date: Date) {
  const nextTick = new Date(date);
  const nextMinutes =
    Math.floor(date.getMinutes() / HEADER_DATA_TIME_STEP_MINUTES) * HEADER_DATA_TIME_STEP_MINUTES +
    HEADER_DATA_TIME_STEP_MINUTES;

  nextTick.setMinutes(nextMinutes, 0, 0);

  return Math.max(nextTick.getTime() - date.getTime(), 1_000);
}

export function MapWorkbenchPage() {
  const hasMapboxToken = Boolean(process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN);
  const devPanelEnabled = process.env.NEXT_PUBLIC_ENABLE_DEV_PANEL === "true";
  const mapContainerRef = useRef<HTMLDivElement | null>(null);
  const desktopAgentPanelRef = useRef<HTMLDivElement | null>(null);
  const agentPanelResizeLeftRef = useRef(0);
  const agentPanelResizeCleanupRef = useRef<(() => void) | null>(null);
  const [detailFeature, setDetailFeature] = useState<DetailFeature | null>(null);
  const [devPanelOpen, setDevPanelOpen] = useState(false);
  const [impactVisible, setImpactVisible] = useState(false);
  const [isLargeScreen, setIsLargeScreen] = useState(false);
  const [viewportWidth, setViewportWidth] = useState<number>(WORKBENCH_LAYOUT.desktopMinWidth);
  const [agentPanelWidth, setAgentPanelWidth] = useState<number | null>(null);
  const [agentPanelResizing, setAgentPanelResizing] = useState(false);
  const [activeToolId, setActiveToolId] = useState<ToolbarToolId | null>(null);
  const [conditionFeedVisible, setConditionFeedVisible] = useState(false);
  const [conditionFeedMounted, setConditionFeedMounted] = useState(true);
  const [conditionFeedExpanded, setConditionFeedExpanded] = useState(false);
  const [taskTickerVisible, setTaskTickerVisible] = useState(true);
  const [selectedConditionId, setSelectedConditionId] = useState<string | null>(null);
  const [conditionFocusRequest, setConditionFocusRequest] = useState<{
    conditionId: string;
    requestId: number;
  } | null>(null);
  const [activeBaseLayerId, setActiveBaseLayerId] = useState(hasMapboxToken ? "mapbox-light" : "none");
  const basemapTone = getWorkbenchBasemapTone(activeBaseLayerId);
  const [activeScenarioId, setActiveScenarioId] = useState(WORKBENCH_SCENARIOS[0]?.id ?? "");
  const [activeMeasureModeId, setActiveMeasureModeId] = useState<WorkbenchMeasureMode>("distance");
  const [activeMeasureUnitId, setActiveMeasureUnitId] = useState<WorkbenchMeasureUnit>("km");
  const [activeDrawToolId, setActiveDrawToolId] = useState<WorkbenchDrawMode | null>(null);
  const [layerVisibility, setLayerVisibility] = useState<Record<string, boolean>>(INITIAL_LAYER_VISIBILITY);
  const [agentUiResults, setAgentUiResults] = useState<AgentUiResult[]>([]);
  const [scheduledConditions, setScheduledConditions] = useState<ScheduledConditionItem[]>([]);
  const [scheduledConditionsLoading, setScheduledConditionsLoading] = useState(true);
  const [headerDataTime, setHeaderDataTime] = useState(INITIAL_HEADER_DATA_TIME);
  const baseLayerOptions = useMemo(
    () =>
      BASE_LAYER_OPTIONS.map((option) => ({
        ...option,
        disabled: option.id !== "none" && !hasMapboxToken
      })),
    [hasMapboxToken]
  );

  const handleSelectFeature = useCallback((feature: DetailFeature) => {
    setDetailFeature(feature);
  }, []);

  useEffect(() => {
    const mediaQuery = window.matchMedia("(min-width: 1024px)");
    const handleChange = () => {
      setIsLargeScreen(mediaQuery.matches);
      setViewportWidth(window.innerWidth);
      setAgentPanelWidth((currentWidth) =>
        currentWidth === null ? null : clampAgentPanelWidth(currentWidth, window.innerWidth)
      );
    };

    handleChange();
    setConditionFeedVisible(window.innerWidth >= 1280);
    mediaQuery.addEventListener("change", handleChange);
    window.addEventListener("resize", handleChange);

    return () => {
      mediaQuery.removeEventListener("change", handleChange);
      window.removeEventListener("resize", handleChange);
    };
  }, []);

  useEffect(() => () => agentPanelResizeCleanupRef.current?.(), []);

  useEffect(() => {
    let timeoutId: number;

    const refreshHeaderDataTime = () => {
      const now = new Date();

      setHeaderDataTime(formatHeaderDataTime(now));
      timeoutId = window.setTimeout(refreshHeaderDataTime, getMsUntilNextHeaderDataTimeTick(now));
    };

    refreshHeaderDataTime();

    return () => window.clearTimeout(timeoutId);
  }, []);

  useEffect(() => {
    const refreshScheduledConditions = () => {
      setScheduledConditions(createOperationalScheduledConditions(new Date()));
      setScheduledConditionsLoading(false);
    };

    refreshScheduledConditions();
    const intervalId = window.setInterval(refreshScheduledConditions, SCHEDULED_CONDITION_REFRESH_INTERVAL_MS);

    return () => window.clearInterval(intervalId);
  }, []);

  const shouldShowConditionFeed = conditionFeedVisible && !activeToolId;

  useEffect(() => {
    if (shouldShowConditionFeed) {
      setConditionFeedMounted(true);
      return;
    }

    const timeoutId = window.setTimeout(() => {
      setConditionFeedMounted(false);
    }, 170);

    return () => window.clearTimeout(timeoutId);
  }, [shouldShowConditionFeed]);

  const agent = useWorkbenchAgent({
    onUiEnvelope: handleAgentUiEnvelope,
    onFrontendAction: handleFrontendAction
  });
  function openAgentPanelForViewport() {
    if (isLargeScreen) {
      agent.expandPanel();
      return;
    }

    agent.openMobilePanel();
  }

  const leftPanelOpen = isLargeScreen && agent.panelOpen;
  const rightPanelOpen = isLargeScreen && (devPanelOpen || shouldShowConditionFeed);

  const { mapRef, mapReady, mapError, sourceStatuses, fitNetworkBounds } = useWorkbenchMap({
    containerRef: mapContainerRef,
    leftPanelOpen,
    rightPanelOpen,
    impactVisible,
    onSelectFeature: handleSelectFeature,
    selectedFeature: detailFeature
  });
  const { controller: mapController, state: mapControllerState } = useWorkbenchMapController({
    mapRef,
    mapReady,
    leftPanelOpen,
    rightPanelOpen
  });

  const activeAgentUiResults = useMemo(
    () => agentUiResults.filter((result) => result.sessionId === agent.sessionId),
    [agent.sessionId, agentUiResults]
  );
  const conditionAlerts = useMemo(
    () => createScheduledConditionAlerts(scheduledConditions),
    [scheduledConditions]
  );
  const runningTickerConditions = useMemo(
    () =>
      scheduledConditions.filter(
        (condition): condition is ScheduledConditionRecord =>
          condition.kind === "condition" && condition.status === "running"
      ),
    [scheduledConditions]
  );
  const workbenchAlerts = conditionAlerts;

  const drawing = useWorkbenchDrawing({
    mapRef,
    mapReady,
    activeMode: activeDrawToolId,
    onModeChange: setActiveDrawToolId
  });

  const measurement = useWorkbenchMeasurement({
    mapRef,
    mapReady,
    mode: activeMeasureModeId,
    unit: activeMeasureUnitId
  });
  const prefersReducedMotion = useReducedMotion();
  const taskTickerAvailable = runningTickerConditions.length > 0;
  const shouldShowTaskTicker = !agent.mobileOpen && !conditionFeedExpanded && taskTickerVisible && taskTickerAvailable;
  const taskTickerEnterState = prefersReducedMotion
    ? {
        opacity: 1,
        scale: 1,
        y: 0
      }
    : {
        opacity: 1,
        scale: 1,
        y: 0,
        transition: {
          type: "spring" as const,
          stiffness: 420,
          damping: 30,
          mass: 0.75
        }
      };
  const taskTickerExitState = prefersReducedMotion
    ? {
        opacity: 0,
        scale: 1,
        y: 0,
        transition: { duration: 0 }
      }
    : {
        opacity: 0,
        scale: 0.96,
        y: -8,
        transition: {
          duration: 0.18,
          ease: [0.5, 0, 0.2, 1] as const
        }
      };

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

    applyBaseLayerVisibility(mapRef.current, activeBaseLayerId);
  }, [activeBaseLayerId, mapReady, mapRef]);

  const toggleToolbarTool = useCallback((toolId: ToolbarToolId) => {
    setActiveToolId((current) => {
      return current === toolId ? null : toolId;
    });
  }, []);

  const handleConditionFocusRequestHandled = useCallback((requestId: number) => {
    setConditionFocusRequest((current) => (current?.requestId === requestId ? null : current));
  }, []);

  const toolbarItems = useMemo<MapToolbarItem[]>(
    () =>
      waterNetworkToolbarItems.map((item) => ({
        ...item,
        active: activeToolId === item.id,
        disabled: !mapReady,
        onClick: () => toggleToolbarTool(item.id as ToolbarToolId)
      })),
    [activeToolId, mapReady, toggleToolbarTool]
  );

  const layerControlItems = useMemo(() => createLayerControlItems(layerVisibility), [layerVisibility]);
  function handleToggleLayer(layerControlId: string, visible: boolean) {
    const map = mapRef.current;

    setLayerVisibility((current) => ({ ...current, [layerControlId]: visible }));
    if (layerControlId === "simulation") {
      setImpactVisible(visible);
    }

    if (!map || !mapReady) {
      return;
    }

    getWorkbenchLayerIds(map, layerControlId).forEach((layerId) => {
      if (map.getLayer(layerId)) {
        map.setLayoutProperty(layerId, "visibility", visible ? "visible" : "none");
      }
    });
  }

  async function handleExportView(preset: ExportViewPreset) {
    const map = mapRef.current;
    if (!map || !mapReady) {
      showMapNotice({ tone: "warning", message: "地图尚未就绪，无法导出视图。" });
      return;
    }

    const targetLongEdge = getExportTargetLongEdge(preset);
    const exportLabel = getExportPresetLabel(preset);

    showMapNotice({
      id: "map-view-export",
      tone: "loading",
      title: "正在导出",
      message: `正在生成${exportLabel}地图截图...`,
      duration: Infinity
    });

    try {
      const result = await exportMapViewImage(map, {
        scale: preset === "current" ? 1 : undefined,
        targetLongEdge,
        selectedFeature: toMapFeatureReference(detailFeature)
      });
      showMapNotice({
        id: "map-view-export",
        tone: "success",
        message: `已导出 ${result.width} × ${result.height} PNG。`
      });
    } catch {
      showMapNotice({
        id: "map-view-export",
        tone: "error",
        title: "导出失败",
        message: "当前底图或瓦片可能限制了画布导出。"
      });
    }
  }

  function handleRefreshTiles() {
    const map = mapRef.current;
    if (!map || !mapReady) {
      showMapNotice({ tone: "warning", message: "地图尚未就绪，无法刷新瓦片。" });
      return;
    }

    map.triggerRepaint();
    showMapNotice({ tone: "success", message: "已请求地图重新渲染，业务瓦片将在视图变化时刷新。" });
  }

  function handleShowDataStatus() {
    if (sourceStatuses.length === 0) {
      showMapNotice({ tone: "info", message: "数据源状态正在初始化。" });
      return;
    }

    showMapNotice({
      tone: sourceStatuses.some((sourceStatus) => sourceStatus.status === "offline") ? "warning" : "info",
      title: "数据状态",
      message: sourceStatuses.map((sourceStatus) => `${sourceStatus.sourceName}：${sourceStatus.status}`).join(" · "),
      duration: 5000
    });
  }

  function handleShowShortcuts() {
    showMapNotice({
      tone: "info",
      title: "快捷键",
      message: "双击完成线/面绘制；圆形标注先点圆心，再点半径；Esc 可作为后续快捷键扩展。",
      duration: 6000
    });
  }

  function handleSelectScenario(scenarioId: string) {
    const scenario = WORKBENCH_SCENARIOS.find((item) => item.id === scenarioId);
    setActiveScenarioId(scenarioId);
    showMapNotice({
      tone: "success",
      title: "方案已切换",
      message: `当前方案：${scenario?.name ?? scenarioId}。地图模拟结果暂未自动切换。`
    });
  }

  function handlePreviewScenario() {
    setImpactVisible(true);
    setLayerVisibility((current) => ({ ...current, simulation: true }));

    if (mapRef.current && mapReady) {
      WORKBENCH_LAYER_GROUPS.simulation.forEach((layerId) => {
        if (mapRef.current?.getLayer(layerId)) {
          mapRef.current.setLayoutProperty(layerId, "visibility", "visible");
        }
      });
    }

    showMapNotice({
      tone: "info",
      title: "预览影响范围",
      message: "已打开模拟结果图层，可在右侧图层面板继续调整显示。"
    });
  }

  function handleCompareScenario() {
    showMapNotice({
      tone: "info",
      title: "方案比较",
      message: "候选方案比较将汇总影响范围、阀门操作与保供风险。"
    });
  }

  function handleExportScenarioReport() {
    showMapNotice({
      tone: "success",
      title: "场景报告",
      message: "已生成当前场景报告任务，可继续导出地图截图或配置。"
    });
  }

  async function handleAnalyzeAlertsWithAgent() {
    if (workbenchAlerts.length === 0) {
      showMapNotice({
        tone: "info",
        title: "当前无待复核工况",
        message: "当前没有需要汇总的工况提醒。"
      });
      return;
    }

    if (agent.streaming) {
      showMapNotice({ tone: "warning", message: "Agent 正在处理上一条指令。" });
      return;
    }

    openAgentPanelForViewport();

    try {
      await agent.submitPrompt(
        createAlertQueueConversationPrompt({
          alerts: workbenchAlerts,
          conditions: scheduledConditions
        })
      );

      showMapNotice({
        tone: "success",
        title: "已发起工况汇总",
        message: "已将待复核工况发送到 Agent。"
      });
    } catch (error) {
      showMapNotice({
        tone: "error",
        title: "工况汇总发起失败",
        message: error instanceof Error ? error.message : "无法将待复核工况发送到 Agent。"
      });
    }
  }

  function handleSelectAlert(alert: WorkbenchAlert) {
    const conditionId = getScheduledConditionIdFromAlertId(alert.id);
    if (conditionId) {
      const condition = scheduledConditions.find((item) => item.id === conditionId);
      if (!condition) {
        showMapNotice({
          tone: "warning",
          title: "工况任务未找到",
          message: `未找到提醒关联的工况任务：${conditionId}。`
        });
        return;
      }

      setConditionFeedVisible(true);
      setActiveToolId(null);
      setConditionFocusRequest((current) => ({
        conditionId,
        requestId: (current?.requestId ?? 0) + 1
      }));
      return;
    }

    showMapNotice({
      tone: "warning",
      title: alert.title,
      message: alert.description
    });
  }

  async function handleAgentUiEnvelope(payload: UIEnvelopePayload, sessionId: string) {
    const { envelope } = payload;

    if (envelope.kind === "map_action") {
      await handleAgentMapAction(envelope.action, envelope.params, sessionId, envelope.fallbackText);
      return;
    }

    setAgentUiResults((current) => [
      ...current.slice(-5),
      {
        id: payload.envelope_id || `agent-ui-${Date.now().toString(36)}`,
        sessionId,
        createdAt: payload.created_at,
        envelope
      }
    ]);

    showMapNotice({
      tone: "info",
      title: envelope.kind === "chart" ? "Agent 图表已渲染" : "Agent 面板已渲染",
      message:
        envelope.fallbackText ??
        (envelope.kind === "chart" ? "已在 Agent 面板显示可信图表。" : "已在 Agent 面板显示可信组件。")
    });
  }

  async function handleFrontendAction(request: FrontendActionRequest, signal: AbortSignal) {
    if (request.name === "render_scada_analysis") {
      const items = parseScadaAnalysisItems(request.params.items);
      if (!items) throw new Error("INVALID_SCADA_ANALYSIS");
      const result = await mapController.renderScadaAnalysis(items, signal);
      const counts = result.level_counts;
      showMapNotice({
        tone: "success",
        title: "Agent SCADA 分析已渲染",
        message: `已渲染 ${result.rendered_ids.length} 个，高 ${counts.high}、中 ${counts.medium}、低 ${counts.low}${result.missing_ids.length ? `，缺失 ${result.missing_ids.length} 个` : ""}。`
      });
      return result;
    }
    if (request.name === "clear_scada_analysis") {
      const result = mapController.clearScadaAnalysis();
      showMapNotice({
        tone: "info",
        title: "Agent SCADA 分析已清除",
        message: "地图已移除当前会话的 SCADA 分析结果。"
      });
      return result;
    }
    throw new Error("UNSUPPORTED_FRONTEND_ACTION");
  }

  async function handleStartNewAgentSession() {
    mapController.clearScadaAnalysis();
    await agent.startNewSession();
  }

  async function handleLoadAgentHistorySession(sessionId: string) {
    if (sessionId !== agent.sessionId) {
      mapController.clearScadaAnalysis();
    }
    await agent.loadHistorySession(sessionId);
  }

  async function handleDeleteAgentHistorySession(sessionId: string) {
    const clearsActiveAnalysis = sessionId === agent.sessionId;
    const deleted = await agent.deleteHistorySession(sessionId);
    if (deleted && clearsActiveAnalysis) {
      mapController.clearScadaAnalysis();
    }
  }

  async function handleAgentMapAction(action: string, params: unknown, sessionId: string, fallbackText?: string) {
    const trustedAction = toTrustedMapAction(action, params, fallbackText);
    if (!trustedAction) {
      showMapNotice({
        tone: "warning",
        title: "地图动作已降级",
        message: fallbackText ?? "Agent 地图动作参数未通过前端校验。"
      });
      return;
    }

    if (trustedAction.type === "zoom_to_map") {
      const map = mapRef.current;
      if (!map || !mapReady) {
        showMapNotice({ tone: "warning", message: "地图尚未就绪，无法执行 Agent 缩放动作。" });
        return;
      }

      map.easeTo({
        center: trustedAction.center,
        zoom: trustedAction.zoom ?? Math.max(map.getZoom(), 14),
        padding: getResponsiveWorkbenchPadding(map, leftPanelOpen, rightPanelOpen),
        duration: trustedAction.durationMs ?? 500
      });
      showMapNotice({
        tone: "info",
        title: "Agent 地图定位",
        message: trustedAction.fallbackText ?? "已根据 Agent 建议缩放到目标位置。"
      });
      return;
    }

    if (trustedAction.type === "locate_features") {
      fitNetworkBounds();
      showMapNotice({
        tone: "info",
        title: "Agent 资产定位",
        message:
          trustedAction.fallbackText ??
          (trustedAction.featureIds.length > 0
            ? `已接收 ${trustedAction.featureIds.length} 个目标资产，当前先回到全网视图。`
            : "已根据 Agent 建议回到全网视图。")
      });
      return;
    }

    if (trustedAction.type === "apply_layer_style") {
      if (trustedAction.layerGroupId && typeof trustedAction.visible === "boolean") {
        handleToggleLayer(trustedAction.layerGroupId, trustedAction.visible);
      }

      showMapNotice({
        tone: "info",
        title: "Agent 图层样式",
        message: trustedAction.fallbackText ?? "已接收 Agent 图层样式建议，当前仅执行受控图层显隐。"
      });
      return;
    }

    try {
      await agent.resolveRenderRef(trustedAction.renderRef, sessionId);
      showMapNotice({
        tone: "info",
        title: "Agent 节点渲染",
        message: trustedAction.fallbackText ?? "已验证 render_ref，节点分类渲染器将在后续接入。"
      });
    } catch (error) {
      showMapNotice({
        tone: "error",
        title: "render_ref 解析失败",
        message: error instanceof Error ? error.message : "无法读取 Agent 渲染结果。"
      });
    }
  }

  function handleExportConfig() {
    const config = {
      exportedAt: new Date().toISOString(),
      activeBaseLayerId,
      layerVisibility,
      activeToolId,
      activeMeasureModeId,
      activeMeasureUnitId
    };
    const blob = new Blob([JSON.stringify(config, null, 2)], { type: "application/json" });
    const url = URL.createObjectURL(blob);
    const link = document.createElement("a");
    link.href = url;
    link.download = "drainage-network-map-config.json";
    document.body.appendChild(link);
    link.click();
    link.remove();
    URL.revokeObjectURL(url);
    showMapNotice({ tone: "success", message: "当前地图配置已导出。" });
  }

  const toolbarPanelProps: ToolbarPanelProps = {
    activeToolId,
    activeMeasureModeId,
    activeMeasureUnitId,
    measuring: measurement.measuring,
    measureResult: measurement.result,
    measureEnabled: mapReady,
    hasMeasurePoints: measurement.hasPoints,
    activeDrawToolId,
    drawingEnabled: mapReady,
    drawingAnnotations: drawing.annotations,
    canUndoDrawing: drawing.canUndo,
    canRedoDrawing: drawing.canRedo,
    canDeleteSelectedDrawing: drawing.canDeleteSelected,
    hasDrawingFeatures: drawing.hasFeatures,
    layerItems: layerControlItems,
    legendItems: MAP_LEGEND_ITEMS,
    baseLayerOptions,
    activeBaseLayerId,
    onSelectMeasureMode: setActiveMeasureModeId,
    onSelectMeasureUnit: setActiveMeasureUnitId,
    onStartMeasure: measurement.start,
    onStopMeasure: measurement.stop,
    onClearMeasure: measurement.clear,
    onCopyMeasure: measurement.copyResult,
    onSelectDrawTool: setActiveDrawToolId,
    onUndoDrawing: drawing.undo,
    onRedoDrawing: drawing.redo,
    onDeleteSelectedDrawing: drawing.deleteSelected,
    onClearDrawing: drawing.clear,
    onExportAnnotations: drawing.exportGeoJSON,
    onSelectBaseLayer: setActiveBaseLayerId,
    onToggleLayer: handleToggleLayer,
    onExportView: handleExportView,
    onRefreshTiles: handleRefreshTiles,
    onShowDataStatus: handleShowDataStatus,
    onShowShortcuts: handleShowShortcuts,
    onExportConfig: handleExportConfig
  };
  const agentPanelProps: Omit<ComponentProps<typeof AgentCommandPanel>, "collapsing" | "onCollapse"> = {
    personaState: agent.personaState,
    sessionTitle: agent.sessionTitle,
    sessionHistory: agent.sessionHistory,
    sessionHistoryLoading: agent.sessionHistoryLoading,
    activeSessionId: agent.sessionId,
    statusLabel: agent.statusLabel,
    streaming: agent.streaming,
    messages: agent.messages,
    streamRenderState: agent.streamRenderState,
    modelOptions: agent.modelOptions,
    selectedModel: agent.selectedModel,
    approvalMode: agent.approvalMode,
    uiResults: activeAgentUiResults,
    onRefreshHistory: agent.refreshSessionHistory,
    onStartNewSession: handleStartNewAgentSession,
    onLoadHistorySession: handleLoadAgentHistorySession,
    onRenameHistorySession: agent.renameHistorySession,
    onDeleteHistorySession: handleDeleteAgentHistorySession,
    onSelectModel: agent.setSelectedModel,
    onApprovalModeChange: agent.setApprovalMode,
    onSubmitPrompt: agent.submitPrompt,
    onStopPrompt: agent.stopPrompt,
    onReplyPermission: agent.replyPermission,
    onReplyQuestion: agent.replyQuestion,
    onRejectQuestion: agent.rejectQuestion
  };

  const resizeAgentPanel = useCallback((clientX: number) => {
    setAgentPanelWidth(clampAgentPanelWidth(clientX - agentPanelResizeLeftRef.current, window.innerWidth));
  }, []);

  const handleAgentPanelResizeStart = useCallback((event: ReactPointerEvent<HTMLDivElement>) => {
    if (event.button !== 0 || !desktopAgentPanelRef.current) {
      return;
    }

    event.preventDefault();
    agentPanelResizeLeftRef.current = desktopAgentPanelRef.current.getBoundingClientRect().left;
    setAgentPanelResizing(true);
    resizeAgentPanel(event.clientX);

    agentPanelResizeCleanupRef.current?.();
    const handlePointerMove = (pointerEvent: PointerEvent) => {
      pointerEvent.preventDefault();
      resizeAgentPanel(pointerEvent.clientX);
    };
    const stopResizing = () => {
      agentPanelResizeCleanupRef.current?.();
      setAgentPanelResizing(false);
    };
    const cleanup = () => {
      window.removeEventListener("pointermove", handlePointerMove);
      window.removeEventListener("pointerup", stopResizing);
      window.removeEventListener("pointercancel", stopResizing);
      agentPanelResizeCleanupRef.current = null;
    };

    agentPanelResizeCleanupRef.current = cleanup;
    window.addEventListener("pointermove", handlePointerMove);
    window.addEventListener("pointerup", stopResizing);
    window.addEventListener("pointercancel", stopResizing);
  }, [resizeAgentPanel]);

  const handleAgentPanelResizeKeyDown = useCallback((event: ReactKeyboardEvent<HTMLDivElement>) => {
    const currentWidth = desktopAgentPanelRef.current?.getBoundingClientRect().width ?? WORKBENCH_LAYOUT.desktop.agentWidth;
    let nextWidth = currentWidth;

    if (event.key === "ArrowLeft") {
      nextWidth -= 16;
    } else if (event.key === "ArrowRight") {
      nextWidth += 16;
    } else if (event.key === "Home") {
      nextWidth = getWorkbenchViewportLayout(window.innerWidth).agentWidth;
    } else if (event.key === "End") {
      nextWidth = window.innerWidth * WORKBENCH_LAYOUT.maxAgentViewportRatio;
    } else {
      return;
    }

    event.preventDefault();
    setAgentPanelWidth(clampAgentPanelWidth(nextWidth, window.innerWidth));
  }, []);

  return (
    <main
      className="relative h-[100dvh] min-h-[640px] overflow-hidden bg-slate-100 text-slate-900 tabular-nums"
      data-basemap-tone={basemapTone}
      style={WORKBENCH_LAYOUT_CSS_VARIABLES as CSSProperties}
    >
      <div
        ref={mapContainerRef}
        className="map-grid"
        style={{ position: "absolute", inset: 0 }}
        aria-label="排水管网地图"
      />

      <WorkbenchTopBar
        dataTime={headerDataTime}
        modelName="DrainFlow 1.0.0"
        scenarios={WORKBENCH_SCENARIOS}
        activeScenarioId={activeScenarioId}
        alerts={workbenchAlerts}
        user={WORKBENCH_USER}
        conditionFeedVisible={shouldShowConditionFeed}
        taskTickerAvailable={taskTickerAvailable}
        taskTickerVisible={taskTickerVisible}
        devPanelEnabled={devPanelEnabled}
        devPanelOpen={devPanelOpen}
        onSelectScenario={handleSelectScenario}
        onSelectAlert={handleSelectAlert}
        onToggleConditionFeed={() => {
          setConditionFeedVisible((current) => !current);
          setActiveToolId(null);
        }}
        onToggleTaskTicker={() => setTaskTickerVisible((current) => !current)}
        onToggleDevPanel={() => setDevPanelOpen((current) => !current)}
        onPreviewScenario={handlePreviewScenario}
        onCompareScenario={handleCompareScenario}
        onExportScenarioReport={handleExportScenarioReport}
        onAnalyzeAlertsWithAgent={handleAnalyzeAlertsWithAgent}
        onShowDataStatus={handleShowDataStatus}
        onRefreshTiles={handleRefreshTiles}
        onShowShortcuts={handleShowShortcuts}
        onExportConfig={handleExportConfig}
      />

      <div
        ref={desktopAgentPanelRef}
        className="pointer-events-none absolute bottom-4 left-3 top-24 z-20 hidden w-[var(--workbench-agent-width)] max-w-[50vw] 2xl:w-[var(--workbench-agent-width-wide)] lg:block"
        style={agentPanelWidth === null ? undefined : { width: agentPanelWidth }}
      >
        {agent.panelOpen ? (
          <>
            <AgentCommandPanel
              {...agentPanelProps}
              collapsing={agent.panelCollapsing}
              onCollapse={agent.collapsePanel}
            />
            <div
              aria-label="调整 Agent 面板宽度"
              aria-orientation="vertical"
              aria-valuemax={Math.round(viewportWidth * WORKBENCH_LAYOUT.maxAgentViewportRatio)}
              aria-valuemin={getWorkbenchViewportLayout(viewportWidth).agentWidth}
              aria-valuenow={Math.round(agentPanelWidth ?? getWorkbenchViewportLayout(viewportWidth).agentWidth)}
              className={cn(
                "group pointer-events-auto absolute -right-3 top-4 bottom-4 z-10 flex w-6 cursor-ew-resize touch-none items-center justify-center outline-none",
                agentPanelResizing && "[&>span]:bg-blue-500 [&>span]:opacity-100"
              )}
              role="separator"
              tabIndex={0}
              title="拖拽调整 Agent 面板宽度"
              onKeyDown={handleAgentPanelResizeKeyDown}
              onPointerDown={handleAgentPanelResizeStart}
            >
              <span className="h-14 w-1 rounded-full bg-slate-500/60 opacity-50 shadow-sm transition-[height,background-color,opacity] group-hover:h-20 group-hover:bg-blue-500 group-hover:opacity-100 group-focus-visible:h-20 group-focus-visible:bg-blue-500 group-focus-visible:opacity-100" />
            </div>
          </>
        ) : (
          <AgentCollapsedRail
            personaState={agent.personaState}
            statusLabel={agent.statusLabel}
            onExpand={agent.expandPanel}
          />
        )}
      </div>

      {!devPanelOpen ? <div className="absolute right-2 top-24 z-20">
        <MapToolbar items={toolbarItems} className="hidden lg:block" />
      </div> : null}

      {conditionFeedMounted && !devPanelOpen ? (
        <div className="absolute right-16 top-24 z-20 hidden lg:block 2xl:[--workbench-condition-width:var(--workbench-condition-width-wide)]">
          <ScheduledConditionFeed
            conditions={scheduledConditions}
            expanded={conditionFeedExpanded}
            focusRequest={conditionFocusRequest}
            loading={scheduledConditionsLoading}
            visible={shouldShowConditionFeed}
            selectedConditionId={selectedConditionId}
            onExpandedChange={setConditionFeedExpanded}
            onFocusRequestHandled={handleConditionFocusRequestHandled}
            onSelectedConditionChange={setSelectedConditionId}
            onExpandAgent={openAgentPanelForViewport}
            onLoadHistorySession={handleLoadAgentHistorySession}
            onSubmitPrompt={agent.submitPrompt}
          />
        </div>
      ) : null}

      {!devPanelOpen ? <div className="absolute right-16 top-24 z-20 hidden lg:block">
        <ToolbarPanel {...toolbarPanelProps} />
      </div> : null}

      {devPanelEnabled && devPanelOpen ? (
        <MapDevPanel
          commands={mapController}
          controllerState={mapControllerState}
          detailFeature={detailFeature}
          onClose={() => setDevPanelOpen(false)}
        />
      ) : null}

      <div className="absolute bottom-20 left-3 right-3 z-30 lg:hidden">
        {agent.mobileOpen ? (
          <div className="h-[calc(100dvh-8.5rem)] min-h-[420px]">
            <AgentCommandPanel
              {...agentPanelProps}
              collapsing={agent.mobilePanelCollapsing}
              onCollapse={agent.closeMobilePanel}
            />
          </div>
        ) : null}
      </div>

      {!agent.mobileOpen ? (
        <MobileAgentToggle
          personaState={agent.personaState}
          statusLabel={agent.statusLabel}
          streaming={agent.streaming}
          onOpen={agent.openMobilePanel}
        />
      ) : null}

      {!agent.mobileOpen ? (
        <div className="absolute bottom-20 left-3 right-3 z-30 lg:hidden">
          <ToolbarPanel {...toolbarPanelProps} panelWidthClassName="w-full max-h-[52dvh] overflow-y-auto" />
        </div>
      ) : null}

      <div className="pointer-events-none absolute bottom-0 right-0 z-30 hidden flex-col items-end gap-2 md:flex">
        <div className="flex items-end gap-2 pr-2">
          <MapZoom mapRef={mapRef} mapReady={mapReady} onHome={fitNetworkBounds} />
        </div>
        <MapScaleLine mapRef={mapRef} mapReady={mapReady} />
      </div>

      <AnimatePresence initial={false}>
        {shouldShowTaskTicker ? (
          <motion.div
            key="agent-task-ticker-shell"
            className="fixed left-1/2 right-auto top-24 z-20 w-[calc(100%-1.5rem)] [translate:-50%_0] md:absolute md:w-[var(--workbench-ticker-width)] 2xl:w-[var(--workbench-ticker-width-wide)]"
            style={{ transformOrigin: "top center" }}
            initial={
              prefersReducedMotion
                ? false
                : {
                    opacity: 0,
                    scale: 0.96,
                    y: -8
                  }
            }
            animate={taskTickerEnterState}
            exit={taskTickerExitState}
          >
            <AgentTaskTicker
              className="w-full max-w-full"
              conditions={runningTickerConditions}
            />
          </motion.div>
        ) : null}
      </AnimatePresence>

      <div className="absolute bottom-3 left-3 right-3 z-20 md:hidden">
        {!agent.mobileOpen ? (
          <MapToolbar items={toolbarItems} orientation="horizontal" className="w-full justify-center" />
        ) : null}
      </div>

      <FeaturePopover feature={detailFeature} onClose={() => setDetailFeature(null)} />

      {!mapReady && !mapError ? <MapLoadingNotice message="正在加载底图样式、管网数据源和交互图层。" /> : null}
      {!hasMapboxToken ? (
        <MapSourceStatusNotice
          sourceName="Mapbox 底图"
          status="degraded"
          message="未检测到 Mapbox token，当前已切换为无底图模式，仅展示业务图层。"
        />
      ) : null}
      {sourceStatuses.filter(isNoticeSourceStatus).map((sourceStatus) => (
        <MapSourceStatusNotice
          key={sourceStatus.id}
          id={`map-source-runtime-${sourceStatus.id}`}
          sourceName={sourceStatus.sourceName}
          status={sourceStatus.status}
          message={sourceStatus.message}
        />
      ))}
      {mapError ? <MapErrorNotice message={mapError} /> : null}

    </main>
  );
}

function isNoticeSourceStatus(sourceStatus: {
  status: string;
}): sourceStatus is { id: string; sourceName: string; status: MapSourceStatus; message: string } {
  return sourceStatus.status === "online" || sourceStatus.status === "degraded" || sourceStatus.status === "offline";
}

function applyBaseLayerVisibility(map: MapLibreMap, activeBaseLayerId: string) {
  BASE_LAYER_IDS.forEach((layerId) => {
    if (map.getLayer(layerId)) {
      map.setLayoutProperty(layerId, "visibility", activeBaseLayerId === layerId ? "visible" : "none");
    }
  });
}

function getExportTargetLongEdge(preset: ExportViewPreset) {
  if (preset === "4k") {
    return 3840;
  }

  return undefined;
}

function getExportPresetLabel(preset: ExportViewPreset) {
  if (preset === "4k") {
    return " 4K ";
  }

  return "当前分辨率";
}

function createScheduledConditionAlerts(conditions: ScheduledConditionItem[]): WorkbenchAlert[] {
  return conditions
    .filter(isAlertCondition)
    .sort((a, b) => getScheduledConditionTimestamp(b) - getScheduledConditionTimestamp(a))
    .map((condition) => {
      return {
        id: `scheduled-condition-alert-${condition.id}`,
        title: `待复核：${condition.title}`,
        description: condition.detail ?? condition.summary,
        time: formatScheduledConditionTime(condition.scheduledAt)
      };
    });
}

function isAlertCondition(condition: ScheduledConditionItem) {
  return condition.kind === "condition" && condition.status === "error";
}

function getScheduledConditionTimestamp(condition: ScheduledConditionItem) {
  const scheduledAt = Date.parse(condition.scheduledAt);
  return Number.isNaN(scheduledAt) ? condition.updatedAt : scheduledAt;
}

function formatScheduledConditionTime(value: string) {
  const match = value.match(/T(\d{2}:\d{2})/);
  return match?.[1] ?? value;
}

const SCHEDULED_CONDITION_ALERT_ID_PREFIX = "scheduled-condition-alert-";

function getScheduledConditionIdFromAlertId(alertId: string) {
  if (!alertId.startsWith(SCHEDULED_CONDITION_ALERT_ID_PREFIX)) {
    return null;
  }

  return alertId.slice(SCHEDULED_CONDITION_ALERT_ID_PREFIX.length);
}

function MobileAgentToggle({
  personaState,
  statusLabel,
  streaming,
  onOpen
}: {
  personaState: ComponentProps<typeof AgentPersona>["state"];
  statusLabel: string;
  streaming: boolean;
  onOpen: () => void;
}) {
  return (
    <div className="absolute bottom-20 left-3 z-30 lg:hidden">
      <button
        type="button"
        aria-label="打开 Agent 面板"
        title="打开 Agent 面板"
        onClick={onOpen}
        className={cn(
          "grid h-12 w-12 place-items-center text-violet-700",
          MAP_MAJOR_PANEL_RADIUS_CLASS_NAME,
          MAP_CONTROL_SURFACE_CLASS_NAME
        )}
      >
        <AgentPersona
          className="pointer-events-none h-10 w-10"
          fallbackClassName="pointer-events-none h-10 w-10"
          state={personaState}
          statusLabel={statusLabel}
          streaming={streaming}
        />
      </button>
    </div>
  );
}
