"use client";

import React, {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react";
import {
  Alert,
  Box,
  Button,
  Chip,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  IconButton,
  Stack,
  Tooltip,
  Typography,
} from "@mui/material";
import {
  AddLocationAlt as AddIcon,
  DeleteOutline as DeleteIcon,
  Download as DownloadIcon,
  EditLocationAlt as ReplaceIcon,
  LocationOn as LocateIcon,
  Map as MapIcon,
  Redo as RedoIcon,
  RestartAlt as ResetIcon,
  Save as SaveIcon,
  Undo as UndoIcon,
} from "@mui/icons-material";
import {
  DataGrid,
  GridToolbar,
  type GridColDef,
  type GridRowSelectionModel,
} from "@mui/x-data-grid";
import { zhCN } from "@mui/x-data-grid/locales";
import Feature, { type FeatureLike } from "ol/Feature";
import Point from "ol/geom/Point";
import VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
import { Circle, Fill, Stroke, Style, Text } from "ol/style";
import { fromLonLat, toLonLat } from "ol/proj";
import { useNotification } from "@refinedev/core";
import { api } from "@/lib/api";
import { config } from "@/config/config";
import { useMap } from "@components/olmap/core/MapComponent";
import { handleMapClickSelectFeatures } from "@/utils/mapQueryService";
import {
  addSensorPoint,
  createSchemeEditorState,
  deleteSensorPoint,
  isSchemeDirty,
  redoSchemeEdit,
  replaceSensorPoint,
  resetSchemeEdit,
  summarizeChanges,
  toSensorPointRows,
  undoSchemeEdit,
} from "./schemeEditor";
import {
  exportSensorPlacementExcel,
  overwriteSensorPlacementScheme,
} from "./schemeApi";
import SchemeDrawingDialog from "./SchemeDrawingDialog";
import type {
  AdjustmentStatus,
  SensorPlacementScheme,
  SensorPoint,
  SensorPointRow,
} from "./types";

type EditMode = "idle" | "add" | "replace" | "delete";

interface SchemeEditorProps {
  scheme: SensorPlacementScheme;
  network: string;
  active?: boolean;
  onSaved?: (scheme: SensorPlacementScheme) => void;
}

const STATUS_LABELS: Record<AdjustmentStatus, string> = {
  current: "当前方案",
  original: "原方案",
  added: "新增",
  replaced: "替换",
};

const STATUS_COLORS: Record<
  AdjustmentStatus,
  "default" | "success" | "warning" | "info"
> = {
  current: "default",
  original: "info",
  added: "success",
  replaced: "warning",
};

const markerStyle = (feature: FeatureLike) => {
  const status = feature.get("adjustment_status") as AdjustmentStatus;
  const selected = Boolean(feature.get("selected"));
  const color =
    status === "added" ? "#16865b" : status === "replaced" ? "#d06b16" : "#c9252d";
  return new Style({
    image: new Circle({
      radius: selected ? 11 : 9,
      fill: new Fill({ color: "#ffffff" }),
      stroke: new Stroke({ color, width: selected ? 4 : 3 }),
    }),
    text: new Text({
      text: String(feature.get("sequence") ?? ""),
      font: '700 11px -apple-system, "PingFang SC", sans-serif',
      fill: new Fill({ color }),
      offsetY: 0.5,
    }),
    zIndex: selected ? 20 : 10,
  });
};

const errorDescription = (error: unknown) => {
  const candidate = error as {
    response?: { data?: { detail?: string } };
    message?: string;
  };
  return candidate.response?.data?.detail || candidate.message || "请求失败";
};

const SchemeEditor: React.FC<SchemeEditorProps> = ({
  scheme,
  network,
  active = true,
  onSaved,
}) => {
  const map = useMap();
  const { open } = useNotification();
  const [editor, setEditor] = useState(() => createSchemeEditorState(scheme));
  const [mode, setMode] = useState<EditMode>("idle");
  const [selectedNodeId, setSelectedNodeId] = useState<string | null>(null);
  const [replaceSourceId, setReplaceSourceId] = useState<string | null>(null);
  const [saving, setSaving] = useState(false);
  const [exporting, setExporting] = useState(false);
  const [saveDialogOpen, setSaveDialogOpen] = useState(false);
  const [drawingOpen, setDrawingOpen] = useState(false);
  const markerLayerRef = useRef<VectorLayer<VectorSource> | null>(null);

  useEffect(() => {
    setEditor(createSchemeEditorState(scheme));
    setMode("idle");
    setReplaceSourceId(null);
    setSelectedNodeId(null);
  }, [scheme]);

  const rows = useMemo(() => toSensorPointRows(editor), [editor]);
  const dirty = isSchemeDirty(editor);
  const changes = summarizeChanges(editor);

  useEffect(() => {
    if (!map) return;
    const layer = new VectorLayer({
      source: new VectorSource(),
      style: markerStyle,
      properties: {
        name: "监测点方案编辑",
        value: "sensor_scheme_editor",
        queryable: false,
      },
      zIndex: 120,
    });
    markerLayerRef.current = layer;
    map.addLayer(layer);
    return () => {
      markerLayerRef.current = null;
      map.removeLayer(layer);
    };
  }, [map]);

  useEffect(() => {
    markerLayerRef.current?.setVisible(active);
  }, [active]);

  useEffect(() => {
    const source = markerLayerRef.current?.getSource();
    if (!source) return;
    source.clear();
    rows.forEach((row) => {
      const feature = new Feature({
        geometry: new Point([row.map_x, row.map_y]),
        node_id: row.node_id,
        sequence: row.sequence,
        adjustment_status: row.adjustment_status,
        selected: row.node_id === selectedNodeId,
      });
      feature.setId(`sensor-scheme-${row.node_id}`);
      source.addFeature(feature);
    });
  }, [rows, selectedNodeId]);

  const resolveJunction = useCallback(
    async (event: any): Promise<SensorPoint | null> => {
      if (!map) return null;
      const feature = await handleMapClickSelectFeatures(event, map);
      const nodeId = String(feature?.get("id") ?? feature?.getId() ?? "").trim();
      if (!nodeId) return null;
      const featureGeometry = feature?.getGeometry();
      const featureCoordinate =
        featureGeometry instanceof Point
          ? featureGeometry.getCoordinates()
          : event.coordinate;
      const projectedFeatureCoordinate =
        Math.abs(featureCoordinate[0]) <= 180 &&
        Math.abs(featureCoordinate[1]) <= 90
          ? fromLonLat(featureCoordinate)
          : featureCoordinate;
      const resolution = map.getView().getResolution() ?? 1;
      const isAlignedWithMap =
        Math.hypot(
          projectedFeatureCoordinate[0] - event.coordinate[0],
          projectedFeatureCoordinate[1] - event.coordinate[1],
        ) <=
        resolution * 20;
      const [mapX, mapY] = isAlignedWithMap
        ? projectedFeatureCoordinate
        : event.coordinate;
      try {
        const response = await api.get<{
          id: string;
          x: number;
          y: number;
          elevation: number;
        }>(`${config.BACKEND_URL}/api/v1/junctions/properties`, {
          params: { junction: nodeId },
        });
        if (!response.data?.id) return null;
        const [longitude, latitude] = toLonLat([mapX, mapY]);
        return {
          node_id: String(response.data.id),
          project_x: Number(response.data.x),
          project_y: Number(response.data.y),
          map_x: mapX,
          map_y: mapY,
          longitude,
          latitude,
          elevation: Number(response.data.elevation),
        };
      } catch {
        return null;
      }
    },
    [map],
  );

  useEffect(() => {
    if (!active || !map || mode === "idle" || !scheme.can_edit) return;
    let handling = false;
    const processClick = async (event: any) => {
      if (handling) return;
      event.stopPropagation?.();
      handling = true;
      try {
        const markerLayer = markerLayerRef.current;
        const marker = markerLayer
          ? map.forEachFeatureAtPixel(
              event.pixel,
              (feature) => feature as Feature,
              {
                hitTolerance: 8,
                layerFilter: (layer) => layer === markerLayer,
              },
            )
          : undefined;
        const markerNodeId = marker ? String(marker.get("node_id")) : null;

        if (mode === "delete") {
          if (!markerNodeId) {
            open?.({ type: "error", message: "请点击要删除的监测点" });
            return;
          }
          setEditor((current) => deleteSensorPoint(current, markerNodeId));
          setSelectedNodeId(null);
          return;
        }

        if (mode === "replace" && !replaceSourceId) {
          if (!markerNodeId) {
            open?.({ type: "error", message: "请先点击要替换的监测点" });
            return;
          }
          setReplaceSourceId(markerNodeId);
          setSelectedNodeId(markerNodeId);
          return;
        }

        const candidate = await resolveJunction(event);
        if (!candidate) {
          open?.({ type: "error", message: "请选择有效的管网节点" });
          return;
        }

        if (mode === "add") {
          const exists = editor.points.some(
            (point) => point.node_id === candidate.node_id,
          );
          if (exists) {
            open?.({ type: "error", message: "该节点已在当前方案中" });
            return;
          }
          setEditor((current) => addSensorPoint(current, candidate));
          setSelectedNodeId(candidate.node_id);
        } else if (mode === "replace" && replaceSourceId) {
          const duplicate = editor.points.some(
            (point) =>
              point.node_id === candidate.node_id &&
              point.node_id !== replaceSourceId,
          );
          if (duplicate) {
            open?.({ type: "error", message: "目标节点已在当前方案中" });
            return;
          }
          setEditor((current) =>
            replaceSensorPoint(current, replaceSourceId, candidate),
          );
          setSelectedNodeId(candidate.node_id);
          setReplaceSourceId(null);
          setMode("idle");
        }
      } finally {
        handling = false;
      }
    };
    const handleClick = (event: any) => {
      void processClick(event);
    };
    map.on("singleclick", handleClick);
    return () => {
      map.un("singleclick", handleClick);
    };
  }, [
    editor.points,
    active,
    map,
    mode,
    network,
    open,
    replaceSourceId,
    resolveJunction,
    scheme.can_edit,
  ]);

  const activateMode = useCallback(
    (nextMode: EditMode, sourceNodeId?: string) => {
      setMode((current) =>
        current === nextMode && !sourceNodeId ? "idle" : nextMode,
      );
      setReplaceSourceId(sourceNodeId ?? null);
      if (sourceNodeId) setSelectedNodeId(sourceNodeId);
    },
    [],
  );

  const handleDelete = useCallback((nodeId: string) => {
    setEditor((current) => deleteSensorPoint(current, nodeId));
    if (selectedNodeId === nodeId) setSelectedNodeId(null);
  }, [selectedNodeId]);

  const locateRow = useCallback((row: SensorPointRow) => {
    setSelectedNodeId(row.node_id);
    if (!map) return;
    map.getView().animate({
      center: [row.map_x, row.map_y],
      zoom: Math.max(map.getView().getZoom() ?? 16, 18),
      duration: 300,
    });
  }, [map]);

  const handleSave = async () => {
    setSaving(true);
    try {
      const updated = await overwriteSensorPlacementScheme(
        scheme.id,
        editor.baseline.map((point) => point.node_id),
        editor.points.map((point) => point.node_id),
      );
      setEditor(createSchemeEditorState(updated));
      onSaved?.(updated);
      setSaveDialogOpen(false);
      open?.({
        type: "success",
        message: "方案已覆盖保存",
        description: `当前共 ${updated.sensor_number} 个监测点`,
      });
    } catch (error) {
      open?.({
        type: "error",
        message:
          (error as { response?: { status?: number } }).response?.status === 409
            ? "方案已发生变化"
            : "方案保存失败",
        description: errorDescription(error),
      });
    } finally {
      setSaving(false);
    }
  };

  const handleExport = async () => {
    setExporting(true);
    try {
      const blob = await exportSensorPlacementExcel(
        scheme.id,
        editor.points.map((point) => point.node_id),
        editor.statuses,
      );
      const url = URL.createObjectURL(blob);
      const link = document.createElement("a");
      link.href = url;
      link.download = `${scheme.scheme_name}${dirty ? "_未保存草稿" : ""}_监测点清单.xlsx`;
      document.body.appendChild(link);
      link.click();
      link.remove();
      URL.revokeObjectURL(url);
    } catch (error) {
      open?.({
        type: "error",
        message: "Excel 导出失败",
        description: errorDescription(error),
      });
    } finally {
      setExporting(false);
    }
  };

  const columns = useMemo<GridColDef<SensorPointRow>[]>(
    () => [
      {
        field: "sequence",
        headerName: "序号",
        width: 64,
        align: "center",
        headerAlign: "center",
        sortable: false,
      },
      { field: "node_id", headerName: "节点 ID", minWidth: 120, flex: 0.8 },
      {
        field: "longitude",
        headerName: "经度",
        minWidth: 125,
        flex: 1,
        align: "right",
        headerAlign: "right",
        valueFormatter: (value) => Number(value).toFixed(6),
      },
      {
        field: "latitude",
        headerName: "纬度",
        minWidth: 125,
        flex: 1,
        align: "right",
        headerAlign: "right",
        valueFormatter: (value) => Number(value).toFixed(6),
      },
      {
        field: "project_x",
        headerName: "工程 X",
        minWidth: 130,
        flex: 1,
        align: "right",
        headerAlign: "right",
        valueFormatter: (value) => Number(value).toFixed(3),
      },
      {
        field: "project_y",
        headerName: "工程 Y",
        minWidth: 130,
        flex: 1,
        align: "right",
        headerAlign: "right",
        valueFormatter: (value) => Number(value).toFixed(3),
      },
      {
        field: "map_x",
        headerName: "地图 X",
        minWidth: 130,
        flex: 1,
        align: "right",
        headerAlign: "right",
        valueFormatter: (value) => Number(value).toFixed(3),
      },
      {
        field: "map_y",
        headerName: "地图 Y",
        minWidth: 130,
        flex: 1,
        align: "right",
        headerAlign: "right",
        valueFormatter: (value) => Number(value).toFixed(3),
      },
      {
        field: "elevation",
        headerName: "高程",
        width: 92,
        align: "right",
        headerAlign: "right",
        valueFormatter: (value) => Number(value).toFixed(3),
      },
      {
        field: "adjustment_status",
        headerName: "调整状态",
        width: 108,
        renderCell: ({ value }) => {
          const status = value as AdjustmentStatus;
          return (
          <Chip
            label={STATUS_LABELS[status]}
            color={STATUS_COLORS[status]}
            variant="outlined"
            size="small"
          />
          );
        },
      },
      {
        field: "actions",
        headerName: "操作",
        width: scheme.can_edit ? 138 : 54,
        sortable: false,
        filterable: false,
        renderCell: ({ row }) => (
          <Stack direction="row" spacing={0.25} sx={{ alignItems: "center" }}>
            <Tooltip title="地图定位">
              <IconButton
                aria-label={`定位节点 ${row.node_id}`}
                size="small"
                onClick={() => locateRow(row)}
                sx={{ width: 40, height: 40 }}
              >
                <LocateIcon fontSize="small" />
              </IconButton>
            </Tooltip>
            {scheme.can_edit && (
              <>
                <Tooltip title="替换节点">
                  <IconButton
                    aria-label={`替换节点 ${row.node_id}`}
                    size="small"
                    onClick={() => activateMode("replace", row.node_id)}
                    sx={{ width: 40, height: 40 }}
                  >
                    <ReplaceIcon fontSize="small" />
                  </IconButton>
                </Tooltip>
                <Tooltip title="删除节点">
                  <span>
                    <IconButton
                      aria-label={`删除节点 ${row.node_id}`}
                      size="small"
                      onClick={() => handleDelete(row.node_id)}
                      disabled={rows.length <= 1}
                      sx={{ width: 40, height: 40 }}
                    >
                      <DeleteIcon fontSize="small" />
                    </IconButton>
                  </span>
                </Tooltip>
              </>
            )}
          </Stack>
        ),
      },
    ],
    [activateMode, handleDelete, locateRow, rows.length, scheme.can_edit],
  );

  const instruction =
    mode === "add"
      ? "点击地图中的管网节点添加监测点"
      : mode === "replace"
        ? replaceSourceId
          ? `已选择 ${replaceSourceId}，请点击新的管网节点`
          : "请先点击要替换的监测点"
        : mode === "delete"
          ? "点击地图中的监测点删除"
          : null;

  return (
    <Box sx={{ display: "flex", flexDirection: "column", height: "100%", gap: 1.5 }}>
      <Box
        sx={{
          display: "flex",
          alignItems: "center",
          justifyContent: "space-between",
          gap: 1.5,
          flexWrap: "wrap",
        }}
      >
        <Box sx={{ minWidth: 0 }}>
          <Typography variant="subtitle1" sx={{ fontWeight: 700 }} noWrap>
            {scheme.scheme_name}
          </Typography>
          <Typography variant="caption" color="text.secondary">
            {scheme.username} · {rows.length} 个监测点
            {dirty ? " · 未保存草稿" : ""}
          </Typography>
        </Box>
        <Stack direction="row" spacing={0.75} sx={{ flexWrap: "wrap" }}>
          <Button
            size="small"
            variant="outlined"
            startIcon={<DownloadIcon />}
            onClick={handleExport}
            disabled={exporting}
          >
            {exporting ? "导出中" : "Excel"}
          </Button>
          <Button
            size="small"
            variant="outlined"
            startIcon={<MapIcon />}
            onClick={() => setDrawingOpen(true)}
          >
            工程图
          </Button>
          {scheme.can_edit && (
            <Button
              size="small"
              variant="contained"
              startIcon={<SaveIcon />}
              disabled={!dirty}
              onClick={() => setSaveDialogOpen(true)}
            >
              覆盖保存
            </Button>
          )}
        </Stack>
      </Box>

      {!scheme.can_edit && (
        <Alert severity="info">当前为只读方案，创建人或管理员可以微调并保存。</Alert>
      )}

      {scheme.can_edit && (
        <Box
          sx={{
            display: "flex",
            alignItems: "center",
            gap: 0.75,
            flexWrap: "wrap",
            p: 1,
            bgcolor: "#f3f6f8",
            borderRadius: 2,
          }}
        >
          <Button
            size="small"
            variant={mode === "add" ? "contained" : "text"}
            startIcon={<AddIcon />}
            onClick={() => activateMode("add")}
          >
            添加
          </Button>
          <Button
            size="small"
            variant={mode === "replace" ? "contained" : "text"}
            startIcon={<ReplaceIcon />}
            onClick={() => activateMode("replace")}
          >
            替换
          </Button>
          <Button
            size="small"
            color="error"
            variant={mode === "delete" ? "contained" : "text"}
            startIcon={<DeleteIcon />}
            onClick={() => activateMode("delete")}
          >
            删除
          </Button>
          <Box sx={{ flex: 1 }} />
          <Tooltip title="撤销上一步">
            <span>
              <IconButton
                aria-label="撤销上一步"
                size="small"
                disabled={!editor.history.length}
                onClick={() => setEditor((current) => undoSchemeEdit(current))}
                sx={{ width: 40, height: 40 }}
              >
                <UndoIcon fontSize="small" />
              </IconButton>
            </span>
          </Tooltip>
          <Tooltip title="重做下一步">
            <span>
              <IconButton
                aria-label="重做下一步"
                size="small"
                disabled={!editor.future.length}
                onClick={() => setEditor((current) => redoSchemeEdit(current))}
                sx={{ width: 40, height: 40 }}
              >
                <RedoIcon fontSize="small" />
              </IconButton>
            </span>
          </Tooltip>
          <Tooltip title="重置为服务器版本">
            <span>
              <IconButton
                aria-label="重置为服务器版本"
                size="small"
                disabled={!dirty}
                onClick={() => setEditor((current) => resetSchemeEdit(current))}
                sx={{ width: 40, height: 40 }}
              >
                <ResetIcon fontSize="small" />
              </IconButton>
            </span>
          </Tooltip>
        </Box>
      )}

      {instruction && (
        <Alert
          severity="info"
          action={
            <Button
              color="inherit"
              size="small"
              onClick={() => {
                setMode("idle");
                setReplaceSourceId(null);
              }}
            >
              取消
            </Button>
          }
        >
          {instruction}
        </Alert>
      )}

      <Box sx={{ flex: 1, minHeight: 320 }}>
        <DataGrid
          rows={rows}
          columns={columns}
          localeText={zhCN.components.MuiDataGrid.defaultProps.localeText}
          getRowId={(row) => row.node_id}
          initialState={{ density: "compact" }}
          disableRowSelectionOnClick={false}
          rowSelectionModel={selectedNodeId ? [selectedNodeId] : []}
          onRowSelectionModelChange={(selection: GridRowSelectionModel) =>
            setSelectedNodeId(selection.length ? String(selection[0]) : null)
          }
          onRowDoubleClick={({ row }) => locateRow(row)}
          slots={{ toolbar: GridToolbar }}
          slotProps={{
            toolbar: {
              showQuickFilter: true,
              printOptions: { disableToolbarButton: true },
              csvOptions: { disableToolbarButton: true },
            },
          }}
          sx={{
            borderColor: "rgba(15, 23, 42, 0.10)",
            "& .MuiDataGrid-columnHeaders": { bgcolor: "#edf3f7" },
            "& .MuiDataGrid-cell": {
              borderColor: "rgba(15, 23, 42, 0.06)",
              fontVariantNumeric: "tabular-nums",
            },
            "& .MuiDataGrid-row.Mui-selected": {
              bgcolor: "rgba(37, 125, 212, 0.10)",
            },
          }}
        />
      </Box>

      <Dialog
        open={saveDialogOpen}
        onClose={() => !saving && setSaveDialogOpen(false)}
        aria-labelledby="overwrite-scheme-title"
      >
        <DialogTitle id="overwrite-scheme-title">覆盖当前方案</DialogTitle>
        <DialogContent>
          <Typography variant="body2" sx={{ lineHeight: 1.8 }}>
            保存后将覆盖原方案节点。此次调整包含：新增 {changes.added} 个，替换{" "}
            {changes.replaced} 个，删除 {changes.removed} 个，保存后共 {rows.length} 个。
          </Typography>
        </DialogContent>
        <DialogActions>
          <Button onClick={() => setSaveDialogOpen(false)} disabled={saving}>
            取消
          </Button>
          <Button variant="contained" onClick={handleSave} disabled={saving}>
            {saving ? "保存中" : "确认覆盖"}
          </Button>
        </DialogActions>
      </Dialog>

      <SchemeDrawingDialog
        open={drawingOpen}
        onClose={() => setDrawingOpen(false)}
        scheme={scheme}
        rows={rows}
        network={network}
        map={map ?? null}
        dirty={dirty}
      />
    </Box>
  );
};

export default SchemeEditor;
