"use client";

import React, {
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react";
import {
  Alert,
  Box,
  Button,
  CircularProgress,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  Stack,
  Typography,
} from "@mui/material";
import {
  Download as DownloadIcon,
  Print as PrintIcon,
} from "@mui/icons-material";
import OlMap from "ol/Map";
import View from "ol/View";
import VectorTileLayer from "ol/layer/VectorTile";
import type VectorTileSource from "ol/source/VectorTile";
import type { StyleLike } from "ol/style/Style";
import {
  TileFeatureIndex,
  clipLineStringPartsToExtent,
  lineStringFromFlatCoordinates,
} from "@components/olmap/core/tileFeatureIndex";
import {
  A3_LANDSCAPE_HEIGHT,
  A3_LANDSCAPE_WIDTH,
  canvasToPngBlob,
  getPaddedDrawingExtent,
  renderEngineeringDrawing,
} from "./engineeringDrawing";
import type {
  NetworkDrawingData,
  SensorPlacementScheme,
  SensorPointRow,
} from "./types";

interface SchemeDrawingDialogProps {
  open: boolean;
  onClose: () => void;
  scheme: SensorPlacementScheme;
  rows: SensorPointRow[];
  network: string;
  map: OlMap | null;
  dirty: boolean;
}

const DRAWING_MAP_RENDER_TIMEOUT_MS = 10_000;
const DRAWING_MAP_SIZE: [number, number] = [
  A3_LANDSCAPE_WIDTH - 300,
  A3_LANDSCAPE_HEIGHT - 650,
];

type DrawingPipeLayer = {
  getSource?: () => VectorTileSource | null;
  getExtent?: () => number[] | undefined;
  getStyle?: () => StyleLike | null | undefined;
};

const getDrawingPipeLayer = (map: OlMap) =>
  map.getAllLayers().find((layer) => layer.get("value") === "pipes") as
    DrawingPipeLayer | undefined;

export const fitMapToFullNetwork = (
  map: OlMap,
): [number, number, number, number] => {
  const extent = getDrawingPipeLayer(map)?.getExtent?.();
  if (
    !extent ||
    extent.length !== 4 ||
    !extent.every(Number.isFinite) ||
    extent[0] >= extent[2] ||
    extent[1] >= extent[3]
  ) {
    throw new Error("主地图中未配置有效的管网完整范围");
  }
  const size = map.getSize();
  if (!size) throw new Error("地图尺寸不可用");
  const networkExtent: [number, number, number, number] = [
    extent[0],
    extent[1],
    extent[2],
    extent[3],
  ];
  const view = map.getView();
  const drawingExtent = getPaddedDrawingExtent(
    networkExtent,
    size[0] / size[1],
  );
  view.cancelAnimations();
  view.fit(drawingExtent, {
    padding: [0, 0, 0, 0],
    size,
  });
  return networkExtent;
};

interface DrawingMapSession {
  map: OlMap;
  extent: [number, number, number, number];
  dispose: () => void;
}

const createDrawingMapSession = (mainMap: OlMap): DrawingMapSession => {
  const mainPipeLayer = getDrawingPipeLayer(mainMap);
  const source = mainPipeLayer?.getSource?.();
  const extent = mainPipeLayer?.getExtent?.();
  if (!source) throw new Error("主地图中未找到管网图层");
  if (
    !extent ||
    extent.length !== 4 ||
    !extent.every(Number.isFinite) ||
    extent[0] >= extent[2] ||
    extent[1] >= extent[3]
  ) {
    throw new Error("主地图中未配置有效的管网完整范围");
  }

  const target = document.createElement("div");
  target.setAttribute("aria-hidden", "true");
  Object.assign(target.style, {
    position: "fixed",
    left: "-100000px",
    top: "0",
    width: `${DRAWING_MAP_SIZE[0]}px`,
    height: `${DRAWING_MAP_SIZE[1]}px`,
    pointerEvents: "none",
  });
  document.body.appendChild(target);

  let drawingMap: OlMap | null = null;
  try {
    const networkExtent: [number, number, number, number] = [
      extent[0],
      extent[1],
      extent[2],
      extent[3],
    ];
    const pipeLayer = new VectorTileLayer({
      source,
      style: mainPipeLayer?.getStyle?.(),
    });
    pipeLayer.set("value", "pipes");
    pipeLayer.setExtent(networkExtent);
    drawingMap = new OlMap({
      target,
      pixelRatio: 1,
      view: new View({
        projection: mainMap.getView().getProjection(),
      }),
      layers: [pipeLayer],
      controls: [],
      interactions: [],
    });
    drawingMap.setSize(DRAWING_MAP_SIZE);
    fitMapToFullNetwork(drawingMap);

    let disposed = false;
    return {
      map: drawingMap,
      extent: networkExtent,
      dispose: () => {
        if (disposed) return;
        disposed = true;
        drawingMap?.setTarget(undefined);
        drawingMap?.getLayers().clear();
        target.remove();
      },
    };
  } catch (reason) {
    drawingMap?.setTarget(undefined);
    target.remove();
    throw reason;
  }
};

const waitForDrawingMapRender = (map: OlMap) =>
  new Promise<void>((resolve, reject) => {
    let timeout: ReturnType<typeof setTimeout>;
    const cleanup = () => {
      clearTimeout(timeout);
      map.un("moveend", requestRender);
      map.un("rendercomplete", handleRenderComplete);
    };
    const handleRenderComplete = () => {
      cleanup();
      resolve();
    };
    const requestRender = () => {
      map.once("rendercomplete", handleRenderComplete);
      map.render();
    };

    timeout = setTimeout(() => {
      cleanup();
      reject(new Error("工程图管网加载超时，请稍后重试"));
    }, DRAWING_MAP_RENDER_TIMEOUT_MS);

    if (map.getView().getAnimating()) {
      map.once("moveend", requestRender);
    } else {
      requestRender();
    }
  });

const readNetworkDataFromMap = (
  map: OlMap,
  extent: [number, number, number, number],
): NetworkDrawingData => {
  const pipeLayer = getDrawingPipeLayer(map);
  const source = pipeLayer?.getSource?.();
  if (!source) throw new Error("工程图中未找到管网图层");

  const index = new TileFeatureIndex("engineering-drawing-pipes", source);
  index.scanLoadedTiles();
  const snapshot = index.getSnapshot(map, map.getView().getZoom() ?? 0);
  const paths = snapshot.instances.flatMap((instance) => {
    if (!instance.geometryType.includes("LineString")) return [];
    return clipLineStringPartsToExtent(
      lineStringFromFlatCoordinates(instance.flatCoordinates, instance.stride),
      instance.tileExtent,
    );
  });
  if (!paths.length) throw new Error("工程图管网尚未加载完成，请稍后重试");

  const nodes: string[] = [];
  const links: string[] = [];
  const nodeIds = new globalThis.Map<string, string>();
  const getNodeId = ([x, y]: number[]) => {
    const key = `${x}:${y}`;
    const existing = nodeIds.get(key);
    if (existing) return existing;
    const nodeId = `drawing_node_${nodeIds.size + 1}`;
    nodeIds.set(key, nodeId);
    nodes.push(`${nodeId}:junction:${x}:${y}`);
    return nodeId;
  };

  paths.forEach((path) => {
    for (let index = 1; index < path.length; index += 1) {
      const startId = getNodeId(path[index - 1]);
      const endId = getNodeId(path[index]);
      links.push(`drawing_link_${links.length + 1}:pipe:${startId}:${endId}`);
    }
  });
  return { nodes, links, extent };
};

const downloadBlob = (blob: Blob, filename: string) => {
  const url = URL.createObjectURL(blob);
  const link = document.createElement("a");
  link.href = url;
  link.download = filename;
  document.body.appendChild(link);
  link.click();
  link.remove();
  URL.revokeObjectURL(url);
};

const escapeHtml = (value: string) =>
  value.replace(
    /[&<>"']/g,
    (character) =>
      ({
        "&": "&amp;",
        "<": "&lt;",
        ">": "&gt;",
        '"': "&quot;",
        "'": "&#039;",
      })[character] ?? character,
  );

export const DrawingPreview = ({
  src,
  label,
}: {
  src: string;
  label: string;
}) => (
  <Box
    role="img"
    aria-label={label}
    sx={{
      width: "100%",
      height: "100%",
      backgroundImage: `url("${src}")`,
      backgroundPosition: "center",
      backgroundRepeat: "no-repeat",
      backgroundSize: "contain",
    }}
  />
);

const SchemeDrawingDialog: React.FC<SchemeDrawingDialogProps> = ({
  open,
  onClose,
  scheme,
  rows,
  network,
  map,
  dirty,
}) => {
  const [networkData, setNetworkData] = useState<NetworkDrawingData | null>(
    null,
  );
  const [previewUrl, setPreviewUrl] = useState<string | null>(null);
  const previewUrlRef = useRef<string | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const replacePreviewUrl = useCallback((nextUrl: string | null) => {
    const previousUrl = previewUrlRef.current;
    previewUrlRef.current = nextUrl;
    setPreviewUrl(nextUrl);
    if (previousUrl && previousUrl !== nextUrl) {
      URL.revokeObjectURL(previousUrl);
    }
  }, []);
  const filename = useMemo(
    () => `${scheme.scheme_name}${dirty ? "_未保存草稿" : ""}_布置图.png`,
    [dirty, scheme.scheme_name],
  );

  useEffect(
    () => () => {
      if (previewUrlRef.current) {
        URL.revokeObjectURL(previewUrlRef.current);
        previewUrlRef.current = null;
      }
    },
    [],
  );

  useEffect(() => {
    if (!open) return;
    let cancelled = false;
    setLoading(true);
    setError(null);
    setNetworkData(null);
    replacePreviewUrl(null);
    if (!map) {
      setError("主地图尚未初始化，请稍后重试");
      setLoading(false);
      return;
    }
    let drawingSession: DrawingMapSession;
    try {
      drawingSession = createDrawingMapSession(map);
    } catch (reason) {
      setError(
        reason instanceof Error ? reason.message : "无法适配完整管网范围",
      );
      setLoading(false);
      return;
    }
    waitForDrawingMapRender(drawingSession.map)
      .then(() =>
        readNetworkDataFromMap(
          drawingSession.map,
          drawingSession.extent,
        ),
      )
      .then((data) => {
        if (!cancelled) {
          setNetworkData(data);
        }
      })
      .catch((reason) => {
        if (!cancelled) {
          setError(
            reason instanceof Error ? reason.message : "无法读取工程图管网数据",
          );
        }
      })
      .finally(() => {
        drawingSession.dispose();
        if (!cancelled) setLoading(false);
      });
    return () => {
      cancelled = true;
      drawingSession.dispose();
    };
  }, [map, network, open, replacePreviewUrl]);

  useEffect(() => {
    if (!open || !networkData) return;
    let cancelled = false;
    setLoading(true);
    setError(null);
    renderEngineeringDrawing({
      scheme,
      rows,
      network,
      networkData,
      dirty,
      width: 1600,
    })
      .then(canvasToPngBlob)
      .then((blob) => {
        if (cancelled) return;
        replacePreviewUrl(URL.createObjectURL(blob));
      })
      .catch((reason) => {
        if (!cancelled)
          setError(reason instanceof Error ? reason.message : "出图失败");
      })
      .finally(() => {
        if (!cancelled) setLoading(false);
      });
    return () => {
      cancelled = true;
    };
  }, [
    dirty,
    network,
    networkData,
    open,
    rows,
    scheme,
    replacePreviewUrl,
  ]);

  const createFullResolutionBlob = async () => {
    if (!networkData) throw new Error("管网数据尚未加载");
    const canvas = await renderEngineeringDrawing({
      scheme,
      rows,
      network,
      networkData,
      dirty,
      width: A3_LANDSCAPE_WIDTH,
    });
    return canvasToPngBlob(canvas);
  };

  const handleDownload = async () => {
    setLoading(true);
    setError(null);
    try {
      downloadBlob(await createFullResolutionBlob(), filename);
    } catch (reason) {
      setError(reason instanceof Error ? reason.message : "PNG 下载失败");
    } finally {
      setLoading(false);
    }
  };

  const handlePrint = async () => {
    const printWindow = window.open("", "_blank");
    if (!printWindow) {
      setError("浏览器阻止了打印窗口，请允许本站打开新窗口");
      return;
    }
    printWindow.opener = null;
    printWindow.document.write(
      '<!doctype html><html lang="zh-CN"><body style="font-family: sans-serif; padding: 24px;">正在生成 A3 工程图...</body></html>',
    );
    printWindow.document.close();
    setLoading(true);
    setError(null);
    try {
      const blob = await createFullResolutionBlob();
      const url = URL.createObjectURL(blob);
      const safeSchemeName = escapeHtml(scheme.scheme_name);
      printWindow.document.open();
      printWindow.document.write(`
        <!doctype html>
        <html lang="zh-CN">
          <head>
            <title>${safeSchemeName} 布置图</title>
            <style>
              @page { size: A3 landscape; margin: 0; }
              html, body { margin: 0; width: 100%; height: 100%; }
              img { display: block; width: 100%; height: 100%; object-fit: contain; }
            </style>
          </head>
          <body><img src="${url}" alt="${safeSchemeName} 压力监测点布置图"></body>
        </html>
      `);
      printWindow.document.close();
      const image = printWindow.document.querySelector("img");
      image?.addEventListener("load", () => {
        printWindow.focus();
        printWindow.print();
        URL.revokeObjectURL(url);
      });
    } catch (reason) {
      printWindow.close();
      setError(reason instanceof Error ? reason.message : "打印失败");
    } finally {
      setLoading(false);
    }
  };

  return (
    <Dialog
      open={open}
      onClose={onClose}
      fullWidth
      maxWidth="lg"
      aria-labelledby="scheme-drawing-title"
      PaperProps={{ sx: { borderRadius: 3, overflow: "hidden" } }}
    >
      <DialogTitle id="scheme-drawing-title" sx={{ pb: 1 }}>
        工程布置图预览
      </DialogTitle>
      <DialogContent sx={{ bgcolor: "#eef2f5", p: 0 }}>
        <Box data-testid="drawing-content-spacing" sx={{ px: 3, pt: 1.5, pb: 2 }}>
          <Box
            data-testid="drawing-format-summary"
            sx={{
              display: "flex",
              alignItems: "center",
              justifyContent: "space-between",
              columnGap: 2,
              rowGap: 0.75,
              mb: 1.5,
              px: 1.5,
              py: 1,
              minHeight: 44,
              bgcolor: "rgba(37, 125, 212, 0.06)",
              border: "1px solid rgba(37, 125, 212, 0.14)",
              borderRadius: 2,
              flexWrap: "wrap",
            }}
          >
          <Typography variant="body2" sx={{ lineHeight: 1.5 }}>
            <Box component="span" sx={{ color: "text.secondary", mr: 1 }}>
              输出样式
            </Box>
            <Box component="span" sx={{ color: "text.primary", fontWeight: 700 }}>
              简化管网线稿
            </Box>
          </Typography>
          <Stack
            direction="row"
            spacing={1}
            divider={
              <Box
                aria-hidden="true"
                sx={{ width: 1, height: 14, bgcolor: "rgba(15, 23, 42, 0.14)" }}
              />
            }
            sx={{
              color: "text.secondary",
              fontVariantNumeric: "tabular-nums",
              whiteSpace: "nowrap",
            }}
          >
            <Typography variant="caption">A3 横向</Typography>
            <Typography variant="caption">300 DPI</Typography>
            <Typography variant="caption">4961 × 3508</Typography>
          </Stack>
          </Box>
          {error && (
            <Alert severity="warning" sx={{ mb: 1.5 }}>
              {error}
            </Alert>
          )}
          <Box
            sx={{
              position: "relative",
              aspectRatio: `${4961} / ${3508}`,
              bgcolor: "#fff",
              boxShadow: "0 8px 28px rgba(15, 23, 42, 0.14)",
              overflow: "hidden",
            }}
          >
            {previewUrl && (
              <DrawingPreview
                src={previewUrl}
                label={`${scheme.scheme_name} 压力监测点布置图预览`}
              />
            )}
            {loading && (
              <Box
                sx={{
                  position: "absolute",
                  inset: 0,
                  display: "grid",
                  placeItems: "center",
                  bgcolor: "rgba(255,255,255,0.72)",
                }}
              >
                <CircularProgress size={30} />
              </Box>
            )}
          </Box>
        </Box>
      </DialogContent>
      <DialogActions sx={{ px: 3, py: 2 }}>
        <Button onClick={onClose}>关闭</Button>
        <Button
          variant="outlined"
          startIcon={<DownloadIcon />}
          onClick={handleDownload}
          disabled={loading || !networkData}
        >
          下载 PNG
        </Button>
        <Button
          variant="contained"
          startIcon={<PrintIcon />}
          onClick={handlePrint}
          disabled={loading || !networkData}
        >
          打印
        </Button>
      </DialogActions>
    </Dialog>
  );
};

export default SchemeDrawingDialog;
