import {
  useState,
  useImperativeHandle,
  forwardRef,
  useEffect,
  useRef,
  useCallback
} from "react";
import { Modal, Input, Button } from "antd";
import "./mermaid.less";
import { parseMermaidToExcalidraw } from "@excalidraw/mermaid-to-excalidraw";
import { Graph } from "@antv/x6";
import { uuid } from "@repo/utils";

import Rectangle from "@/components/flowchart/process";
import Round from "@/components/basic/roundRectangle";
import Stadium from "@/components/flowchart/terminator";
import DoubleCircle from "@/components/er/multivaluedAttribute";
import Circle from "@/components/flowchart/onPageReference";
import Diamond from "@/components/flowchart/decision";
import { isMermaidFlowchart } from "@/utils";
import mermaid from "mermaid";
import { useModel } from "umi";
import { getDefaultDataByTheme } from "@/config/data";

export enum VERTEX_TYPE {
  RECTANGLE = "rectangle", // 矩形
  ROUND = "round", // 圆角矩形
  STADIUM = "stadium", // 椭圆
  DOUBLECIRCLE = "doublecircle", // 双圆
  CIRCLE = "circle", // 圆
  DIAMOND = "diamond", // 菱形
}

export type MermaidResult = {
  type: "image" | "cell";
  data: any;
  width?: number;
  height?: number;
};

export default forwardRef(function MermaidModal(
  { onChange }: { onChange?: (res?: MermaidResult) => void },
  ref
) {
  const [open, setOpen] = useState(false);
  const [mermaidCode, setMermaidCode] = useState("");
  const resultRef = useRef<MermaidResult>();
  const { pageState } = useModel("appModel");
  const theme = useRef(pageState.theme);

  useEffect(() => {
    theme.current = pageState.theme;
  }, [pageState.theme]);

  useImperativeHandle(ref, () => ({
    open: () => {
      setOpen(true);
    },
    close: () => {
      setOpen(false);
    },
  }));

  const mermaidRef = useRef<HTMLDivElement>(null);

  /**
   * 转换成x6 json
   * @param list
   */
  const toX6Json = (list: any[]) => {
    const cells: any[] = [];
    let comp = Rectangle;
    const idMap: Record<string, string> = {};
    const commonData = getDefaultDataByTheme(theme.current || '1');
    const { nodeDefaultData, edgeDefaultData, ports } = commonData;
    list.forEach((item) => {
      // 节点处理
      if (item.type !== "arrow" && item.type !== "line") {
        switch (item.type) {
          case VERTEX_TYPE.ROUND: {
            comp = Round;
            break;
          }
          case VERTEX_TYPE.STADIUM: {
            comp = Stadium;
            break;
          }
          case VERTEX_TYPE.DOUBLECIRCLE: {
            comp = DoubleCircle;
            break;
          }
          case VERTEX_TYPE.CIRCLE: {
            comp = Circle;
            break;
          }
          case VERTEX_TYPE.DIAMOND: {
            comp = Diamond;
            break;
          }
          default: {
            comp = Rectangle;
            break;
          }
        }
        const id = uuid();
        
        const node = {
          ...comp.node,
          id,
          position: {
            x: parseInt(item.x),
            y: parseInt(item.y),
          },
          width: parseInt(item.width),
          height: parseInt(item.height),
          data: {
            ...comp.node.data,
            ...nodeDefaultData,
            text: {
              ...(comp.node.data?.text || {}),
              ...nodeDefaultData.text,
              fontSize: item.label.fontSize,
            },
            label: item.label.text,
          },
          ports: {
            groups: comp.node?.ports?.groups || ports.groups,
            items: comp.node?.ports?.items || ports.items,
          }
        };
        idMap[item.id] = id;
        cells.push(node);
      } 
      if (item.type === "arrow") {
        // 连线处理
        const edge = {
          ...edgeDefaultData,
          source: { cell: idMap[item.start?.id] },
          target: { cell: idMap[item.end?.id] },
          labels: item?.label
            ? [
                {
                  attrs: {
                    label: {
                      text: item.label.text,
                    },
                  },
                },
              ]
            : [],
        };
        cells.push(edge);
      }
    });

    return cells;
  };

  // Fallback to Svg
  const convertSvgToGraphImage = (svgContainer: HTMLDivElement) => {
    // Extract SVG width and height
    // TODO: make width and height change dynamically based on user's screen dimension
    const svgEl = svgContainer.querySelector("svg");
    if (!svgEl) {
      throw new Error("SVG element not found");
    }
    const rect = svgEl.getBoundingClientRect();
    const width = rect.width;
    const height = rect.height;

    // Set width and height explictly since in firefox it gets set to 0
    // if the width and height are not expilcitly set
    // eg in some cases like er Diagram, gnatt, width and height is set as 100%
    // which sets the dimensions as 0 in firefox and thus the diagram isn't rendered
    svgEl.setAttribute("width", `${width}`);
    svgEl.setAttribute("height", `${height}`);

    // Convert SVG to image
    const mimeType = "image/svg+xml";
    const decoded = unescape(encodeURIComponent(svgEl.outerHTML));
    const base64 = btoa(decoded);
    const dataURL = `data:image/svg+xml;base64,${base64}`;

    return {
      type: "graphImage",
      mimeType,
      dataURL,
      width,
      height,
    };
  };

  const handleSvgToImage = (svg: string) => {
    // Append Svg to DOM
    const svgContainer = document.createElement("div");
    svgContainer.setAttribute(
      "style",
      `opacity: 0; position: relative; z-index: -1;`
    );
    svgContainer.innerHTML = svg;
    svgContainer.id = "mermaid-diagram";
    document.body.appendChild(svgContainer);

    const res = convertSvgToGraphImage(svgContainer);
    svgContainer.remove();
    
    resultRef.current = {
      type: "image",
      data: res?.dataURL,
      width: res.width,
      height: res.height,
    };
    const img = new Image();
    img.src = res?.dataURL;
    img.onload = () => {
      if (mermaidRef.current) {
        mermaidRef.current.innerHTML = "";
        mermaidRef.current.appendChild(img);
      }
    };
  };

  /**
   * 绘制图标
   */
  const drawDiagram = async () => {
    // 获取渲染后的 svg
    try {
      resultRef.current = undefined;
      const { elements, files } = await parseMermaidToExcalidraw(mermaidCode.replaceAll('"', "'"));
      console.log("parse elements:", elements, files);
      // 转换失败，加载图片
      if (files) {
        resultRef.current = {
          type: "image",
          data: files[elements[0].fileId]?.dataURL,
          width: elements[0].width,
          height: elements[0].height,
        };
        const img = new Image();
        img.src = files[elements[0].fileId]?.dataURL;
        img.onload = () => {
          if (mermaidRef.current) {
            mermaidRef.current.innerHTML = "";
            mermaidRef.current.appendChild(img);
          }
        };
      } else {
        // 类图与时序图处理
        if(!isMermaidFlowchart(mermaidCode)) {
          const { svg } = await mermaid.render("mermaid", mermaidCode);
          handleSvgToImage(svg);
          return;
        }
        // 转换json
        if (mermaidRef.current) {
          mermaidRef.current.innerHTML = "";
          const cells = toX6Json(elements);
          resultRef.current = { type: "cell", data: cells };
          const graphInstance = new Graph({
            container: mermaidRef.current,
            grid: false,
            width: mermaidRef.current.clientWidth,
            height: mermaidRef.current.clientHeight,
          });
          cells.forEach((cell) => {
            if(cell?.source && cell?.target) {
              graphInstance.addEdge(cell);
            } else {
              graphInstance.addNode(cell);
            }
          });
          graphInstance.zoomToFit({
            padding: 20,
          });
        }
      }
    } catch (error) {
      console.error("mermaid 格式校验失败:错误信息如下:\n", error);
      let html = `<div class='bl-preview-analysis-fail-block'>
          <div class="fail-title">Mermaid 语法解析失败!</div><br/>
          ${error}<br/><br/>
          你可以尝试前往 Mermaid 官网来校验你的内容, 或者查看<a href='https://mermaid.live/edit' target='_blank'>相关文档</a>
          </div>`;
      if (mermaidRef.current) {
        mermaidRef.current.innerHTML = html;
      }
    }
  };

  useEffect(() => {
    if (mermaidCode.trim()) {
      drawDiagram();
    } else {
      if (mermaidRef.current) {
        mermaidRef.current.innerHTML = "";
      }
    }
  }, [mermaidCode]);

  return (
    <Modal
      open={open}
      width="80%"
      title="Mermaid导入"
      footer={() => {
        return (
          <div className="flex justify-end">
            <Button
              type="primary"
              onClick={() => {
                if (onChange) {
                  onChange(resultRef.current);
                }
                setOpen(false);
              }}
            >
              插入
            </Button>
          </div>
        );
      }}
      onCancel={() => setOpen(false)}
    >
      <div className="tip">
        <i>
          目前仅支持
          <a
            className="text-blue"
            href="https://mermaid.js.org/syntax/flowchart.html"
          >
            流程图
          </a>
          导入，其他图以图形方式引入。
        </i>
      </div>
      <div className="flex gap-24px">
        <div className="left flex-1 rounded-4px h-500px border-solid border-gray-200 overflow-y-auto">
          <Input.TextArea
            autoSize={{ minRows: 10 }}
            placeholder="请输入Mermaid代码"
            className="h-full"
            variant="borderless"
            value={mermaidCode}
            onChange={(e) => setMermaidCode(e.target.value)}
          />
        </div>
        <div className="left flex-1 rounded-4px h-500px border-solid border-gray-200">
          <div className="mermaid-container" ref={mermaidRef}></div>
        </div>
      </div>
    </Modal>
  );
});
