<script setup lang="ts">
import LogicFlow from '@logicflow/core';
import { Control, DndPanel, MiniMap } from '@logicflow/extension';
import { message } from 'ant-design-vue';
import BpmnModeler from 'bpmn-js/lib/Modeler';
import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import DrawerViewJson from './components/drawerViewJson.vue';
import DrawerViewXml from './components/drawerViewXml.vue';
// 下一个节点面板
import NextNodePanel from './components/nextNodePanel.vue';
// edge
import MyBezierEdge from './edge/CustomBezierEdge.ts';
// left panel
import leftSettings from './left-panel/leftInfo.ts';
// nodes
import registerNode from './node/nodeRegister/registerConfig';
// right panel
import RightPanel from './right-panel/index.vue';

import { useElementsInfoStore } from './stores/elementsInfo.ts';
import { useSelectedInfoStore } from './stores/selectedInfo.ts';

import parseBpmnXmlToGraph from './tools/adapterIn.ts';
import generateBpmnXml from './tools/adapterOut.ts';

import decodeBpmnXmlBase64 from './tools/bpmnXmlBase64.ts';

import setupEdgeHighlighter from './tools/edgeHighlighter.ts';

import '@logicflow/core/lib/style/index.css';
import '@logicflow/core/es/index.css';

import '@logicflow/extension/lib/style/index.css';

defineOptions({
  name: 'LogicFlowDesigner',
});

const props = defineProps<{
  flowId?: string
  flowName?: string
  xmlString?: string
}>();

const emit = defineEmits(['save']);
const drawerViewXmlRef = ref<InstanceType<typeof DrawerViewXml> | null>(null);
const drawerViewJsonRef = ref<InstanceType<typeof DrawerViewJson> | null>(null);
const nextNodePanelRef = ref<InstanceType<typeof NextNodePanel> | null>(null);
// 引用右侧面板实例，用于测量宽度以计算 miniMap 的 rightPosition
const rightPanelRef = ref<InstanceType<typeof RightPanel> | null>(null);
const selectedInfoStore = useSelectedInfoStore();
const elementsInfoStore = useElementsInfoStore();

const lf = ref<any>();
provide('lf', lf);
const container = ref<HTMLElement | null>(null);
// ResizeObserver instance 用于监听 RightPanel 宽度变化
let miniMapResizeObserver: ResizeObserver | null = null;
const isListenPropsChange = ref<boolean>(false);

const bpmnModeler = ref<BpmnModeler | null>(null);

onMounted(async () => {
  bpmnModeler.value = new BpmnModeler({
    container: document.getElementById('bpmnCanvas') as HTMLElement,
  });
  // 初始化 LogicFlow 实例
  lf.value = new LogicFlow({
    container: container.value as HTMLElement,
    allowRotation: false,
    allowRotate: false,
    allowResize: false,
    hideAnchors: false,
    outline: true,
    hoverOutline: true,
    nodeSelectedOutline: true,
    edgeSelectedOutline: true,
    isSilentMode: false,
    nodeTextEdit: true,
    adjustEdge: false,
    plugins: [Control, MiniMap, DndPanel],
    pluginsOptions: {
      miniMap: { width: 220, height: 150, rightPosition: 10, isShowCloseIcon: true, bottomPosition: 5 },
    },
    grid: {
      visible: true,
      type: 'dot',
      config: {
        color: '#dfdfdf',
        thickness: 1,
      },
    },
  });
  // 显示 miniMap（插件初始化后），并基于 RightPanel 宽度实时更新其 rightPosition
  nextTick(() => {
    lf.value.extension.miniMap.show();

    try {
      // 获取右侧面板 DOM（尝试读取组件根节点下的 .right-panel 元素）
      const panelRoot: HTMLElement | null = rightPanelRef.value?.$el ?? null;
      const panelEl: HTMLElement | null = panelRoot ? (panelRoot.querySelector('.right-panel') as HTMLElement) || panelRoot : null;

      const updateMiniMapPositionByPanelWidth = (width: number) => {
        // rightPanel wrapper 在样式中有 right: 10px 的间距，保持同样的偏移
        const rightGap = 10;
        const fixWidth = Math.round((width || 0) * 14 / 500);
        const newRight = Math.round((width || 0) + rightGap + fixWidth);
        lf.value.extension.miniMap.updatePosition({ right: newRight });
      };

      if (panelEl) {
        // 初始设置
        updateMiniMapPositionByPanelWidth(panelEl.offsetWidth);
        // 使用 ResizeObserver 监听宽度变化（折叠/展开会触发）
        miniMapResizeObserver = new ResizeObserver((entries) => {
          for (const entry of entries) {
            const w = entry.contentRect?.width ?? (entry.target as HTMLElement).offsetWidth;
            updateMiniMapPositionByPanelWidth(w as number);
          }
        });
        miniMapResizeObserver.observe(panelEl);
      }
    }
    catch (err) {
      // 容错：如果 ResizeObserver 不支持或其他异常，则不要阻塞主流程
      console.warn('设置 RightPanel 宽度监听失败', err);
    }
  });
  // lf.value.extension.miniMap.show();
  // 在控制器上添加导航（小地图-右下角位置）
  lf.value.extension.control.addItem({
    key: 'mini-map',
    iconClass: 'custom-minimap',
    text: '导航',
    onClick: () => {
      lf.value.extension.miniMap.show();
    },
  });
  lf.value.extension.control.addItem({
    key: 'clear',
    iconClass: 'custom-clear',
    text: '清空画布',
    onClick: () => {
      clearFL();
    },
  });
  lf.value.extension.control.addItem({
    key: 'viewxml',
    iconClass: 'custom-xml',
    text: '预览xml',
    onClick: () => {
      viewXml();
    },
  });
  lf.value.extension.control.addItem({
    key: 'viewjson',
    iconClass: 'custom-json',
    text: '预览json',
    onClick: () => {
      viewJson();
    },
  });

  // 设置主题
  lf.value.setTheme({
    background: {
      background: '#f2f4f7', // 设置画布背景色
    },
    // 在连线时，从锚点拖出的直线样式
    anchorLine: {
      stroke: '#b1b1b7',
      strokeWidth: 1,
      strokeDasharray: '3,2',
    },
    nodeText: {
      color: '#000000',
      overflowMode: 'autoWrap',
      lineHeight: 1.2,
      fontSize: 12,
      width: 140,
    },
  });

  // 注册边
  lf.value.batchRegister([MyBezierEdge]);
  // 设置默认的边类型
  lf.value.setDefaultEdgeType('custom-bezier-edge');
  // 注册节点
  registerNode(lf.value);
  // 设置左侧面板的拖拽节点配置
  lf.value.extension.dndPanel.setPatternItems(leftSettings);

  const basinfo = await getBaseData();

  lf.value.render(basinfo.graphData);
  lf.value.translateCenter();

  setupEdgeHighlighter(lf.value);

  // 修改graphModel.flowId并且默认选中流程图本身
  lf.value.graphModel.flowId = basinfo.flowId;
  selectedInfoStore.setSelectId(basinfo.flowId);
  selectedInfoStore.setSelectType('flow');

  // 节点的拖动添加
  lf.value.on('node:dnd-add', (data: any) => {
    // console.log('node:dnd-add 事件触发:', e);
    const nodeData = data?.data;
    const nodeId = nodeData?.id;
    // 保存节点数据
    const info: any = { id: nodeId, type: nodeData.type, title: nodeData.properties.title || '' };
    if (info.type === 'user-task-node' || info.type === 'normal-task-node') {
      info.approveMethod = nodeData.properties.approveMethod;
      info.customConfiguration = nodeData.properties.customConfiguration;
    }
    elementsInfoStore.updateElement(nodeId, info);
    // console.log();
  });

  // 监听边的添加事件
  lf.value.on('edge:add', (e: any) => {
    // console.log('edge:add 事件触发:', e);
    try {
      const edgeData = e?.data;
      const edgeId = edgeData?.id;
      // 保存边数据
      const info = { id: edgeId, type: edgeData.type, title: edgeData.properties.text || '' };
      elementsInfoStore.updateElement(edgeId, info);

      selectedInfoStore.setSelectId(edgeId);
      selectedInfoStore.setSelectType(edgeData.type);
      // 当前边选中 其余边selected设置为false
      lf.value.setProperties(edgeId, { selected: true });
      lf.value.graphModel.edges.forEach((edge: any) => {
        if (edge.id !== edgeId) {
          lf.value.setProperties(edge.id, { selected: false });
        }
      });
    }
    catch (err) {
      console.error('edge:add 事件处理出错', err);
    }
  });

  // 监听节点单击事件
  lf.value.on('node:click', ({ data, e }: any) => {
    // const target = e.target as Element;
    const targetParent = e.target.closest('.lf-node');
    if (targetParent) {
      // console.log('找到目标父元素：', targetParent);
      const rect = targetParent.querySelector('.bf-anchor-rect') as HTMLElement | null;
      const circle = targetParent.querySelector('.bf-anchor-circle-group') as HTMLElement | null;
      // const plus = targetParent.querySelector('.bf-anchor-plus') as HTMLElement | null;
      if (rect) {
        rect.style.display = 'none';
      }
      if (circle) {
        circle.style.display = '';
      }
    }
    // console.log('节点被点击:', target.parentNode?.parentNode?.parentNode);
    // console.log('节点被点击:', data);
    selectedInfoStore.setSelectId(data.id);
    selectedInfoStore.setSelectType(data.type);
    // 点击节点时，取消所有边的selected状态
    lf.value.graphModel.edges.forEach((edge: any) => {
      lf.value.setProperties(edge.id, { selected: false });
    });

    rightPanelRef.value?.openPanel();
    nextNodePanelRef.value?.close?.();
  });

  // 监听边单击事件
  lf.value.on('edge:click', ({ data }: any) => {
    // console.log('边被点击:', data);
    selectedInfoStore.setSelectId(data.id);
    selectedInfoStore.setSelectType(data.type);
    // 当前边选中 其余边selected设置为false
    lf.value.setProperties(data.id, { selected: true });
    lf.value.graphModel.edges.forEach((edge: any) => {
      if (edge.id !== data.id) {
        lf.value.setProperties(edge.id, { selected: false });
      }
    });
    rightPanelRef.value?.openPanel();
    nextNodePanelRef.value?.close?.();
  });

  // 监听画布点击事件
  lf.value.on('blank:click', ({ e }: any) => {
    // console.log('画布被点击:', e);
    selectedInfoStore.setSelectId(lf.value.graphModel.flowId);
    selectedInfoStore.setSelectType('flow');
    // 取消所有边的selected状态
    lf.value.graphModel.edges.forEach((edge: any) => {
      lf.value.setProperties(edge.id, { selected: false });
    });
    rightPanelRef.value?.openPanel();
    nextNodePanelRef.value?.close?.();
  });

  // 监听更新文本（目前针对边，节点的文本更新不在此处监听）
  lf.value.on('text:update', ({ data }: any) => {
    // 更新store
    elementsInfoStore.updateElement(data.id, { title: data.text });
    // 更新properties
    lf.value.setProperties(data.id, { title: data.text });
  });

  // 监听节点拖拽放开（主要为了更新xml数据）
  lf.value.on('node:drop', ({ data }: any) => {
    // console.warn('node:drop event:', data);
    const xmlString = getModelXml();
    emit('save', xmlString);
  });

  // 监听节点鼠标进入
  lf.value.on('node:mouseenter', ({ e }: any) => {
    // console.warn('node:mouseenter', e.target);
    // 为了让他先显示锚点，再做其他操作，使用 setTimeout 延迟 1 毫秒
    setTimeout(() => {
      const targetParent = e.target.closest('.lf-node');
      if (targetParent) {
        // console.log('找到目标父元素：', targetParent, targetParent.className.baseVal);
        // 不是选中的节点才会有下面的逻辑
        if (!targetParent.className.baseVal.includes('lf-node-selected')) {
          const rect = targetParent.querySelector('.bf-anchor-rect') as HTMLElement | null;
          const circle = targetParent.querySelector('.bf-anchor-circle-group') as HTMLElement | null;
          // const plus = targetParent.querySelector('.bf-anchor-plus') as HTMLElement | null;
          if (rect) {
            rect.style.display = 'none';
          }
          if (circle) {
            circle.style.display = '';
          }
          // if (plus) {
          //   plus.style.display = '';
          // }
          const nodeWarpElement = targetParent.querySelector('.node-warp') as HTMLElement | null;
          if (nodeWarpElement) {
            nodeWarpElement.classList.add('boxShadow');
            // operate-panel
            const operatePanel = targetParent.querySelector('.operate-panel') as HTMLElement | null;
            if (operatePanel) {
              operatePanel.style.display = 'flex';
            }
          }
        }
      }
    }, 1);
  });

  // 监听节点鼠标离开
  lf.value.on('node:mouseleave', ({ e }: any) => {
    // console.warn('node:mouseleave', e.target);
    const targetParent = e.target;
    if (!targetParent.className.baseVal.includes('lf-node-selected')) {
      const rect = targetParent.querySelector('.bf-anchor-rect') as HTMLElement | null;
      const circle = targetParent.querySelector('.bf-anchor-circle-group') as HTMLElement | null;
      // const plus = targetParent.querySelector('.bf-anchor-plus') as HTMLElement | null;
      if (rect) {
        rect.style.display = '';
      }
      if (circle) {
        circle.style.display = 'none';
      }
      const nodeWarpElement = targetParent.querySelector('.node-warp') as HTMLElement | null;
      if (nodeWarpElement) {
        nodeWarpElement.classList.remove('boxShadow');
        // operate-panel
        const operatePanel = targetParent.querySelector('.operate-panel') as HTMLElement | null;
        if (operatePanel) {
          operatePanel.style.display = 'none';
        }
      }
    }
  });

  // 自定义锚点被点击
  lf.value.on('bf:anchor:click', (detail: any) => {
    // console.log('自定义锚点被点击:', detail);
    try {
      const ev = detail?.ev;
      let anchorEl: HTMLElement | null = null;
      if (ev && ev.target) {
        const tgt = ev.target as Element;
        anchorEl = (tgt.closest && (tgt.closest('.bf-anchor-circle-group') as HTMLElement)) || (tgt as unknown as HTMLElement);
      }
      nextNodePanelRef.value?.openByAnchor(anchorEl, container.value, detail.nodeId);
    }
    catch (err) {
      console.warn('打开 NextNodePanel 失败', err);
    }
  });

  // 初次渲染完xml保存一下并且开始监听属性变化
  setTimeout(() => {
    isListenPropsChange.value = true;
    const xmlString = getModelXml();
    emit('save', xmlString);
  }, 500);
});

function clearFL() {
  lf.value.clearData();
  // 同时清理elementsInfoStore中的元素信息，只保留流程图本身的信息，并更新选中状态
  const flowInfo = elementsInfoStore.getElementById(lf.value.graphModel.flowId);
  const saveflowInfo = { id: flowInfo.id, type: flowInfo.type, title: flowInfo.title };
  elementsInfoStore.clearElements();
  elementsInfoStore.updateElement(saveflowInfo.id, saveflowInfo);
  selectedInfoStore.setSelectId(saveflowInfo.id);
  selectedInfoStore.setSelectType('flow');
}

function getModelXml() {
  const flowData = lf.value.getGraphRawData();
  const flowInfo = elementsInfoStore.getElementById(lf.value.graphModel.flowId);
  // console.log('getModelXml--->', flowData, flowInfo);
  const needData = JSON.parse(JSON.stringify(flowData));
  // console.log('保存xml', needData, flowInfo);
  const xmlString = generateBpmnXml(needData, flowInfo);
  console.warn('保存xml');
  // console.warn('xmlString---->', xmlString);
  return xmlString;
}

function viewXml() {
  const xmlString = getModelXml();

  try {
    new DOMParser().parseFromString(xmlString, 'text/xml');
    drawerViewXmlRef.value?.open(xmlString);
  }
  catch (err) {
    message.error(`XML语法错误：${(err as Error).message}`);
  }
}

function viewJson() {
  const xmlString = getModelXml();
  drawerViewJsonRef.value?.open(xmlString);
}

// 监听属性变化更新xml数据
watch(() => elementsInfoStore.elements, () => {
  if (isListenPropsChange.value) {
    const xmlString = getModelXml();
    // console.log('elementsInfoStore.elements changed, updated xmlString:', xmlString);
    emit('save', xmlString);
  }
}, { deep: true });

async function getBaseData() {
  let graphData: { nodes: any[], edges: any[] } = { nodes: [], edges: [] };
  let flowId: string = `flow_${Date.now()}`;
  let flowName: string = '新建流程图';
  if (props.flowId) {
    flowId = props.flowId;
  }
  if (props.flowName) {
    flowName = props.flowName;
  }
  if (props.xmlString) {
    const bpmnXml = decodeBpmnXmlBase64(props.xmlString);
    await bpmnModeler.value?.importXML(bpmnXml);
    const { xml } = await bpmnModeler.value!.saveXML({ format: true });
    graphData = parseBpmnXmlToGraph(xml as string);
  }
  if (elementsInfoStore.elements.length === 0) {
    // 初始化流程信息
    const flowInfo = {
      id: flowId,
      type: 'flow',
      title: flowName,
    };
    elementsInfoStore.updateElement(flowId, flowInfo);
  }
  else {
    if (props.flowId) {
      // 更新流程信息
      elementsInfoStore.editFlowId(flowId);
    }
    else {
      flowId = elementsInfoStore.elements.find((pd: any) => pd.type === 'flow')?.id || flowId;
    }
    if (props.flowName) {
      elementsInfoStore.editFlowName(flowName);
    }
    else {
      flowName = elementsInfoStore.elements.find((pd: any) => pd.type === 'flow')?.title || flowName;
    }
  }
  // console.log('初始化流程图基本数据 elementsInfoStore', elementsInfoStore.elements);
  selectedInfoStore.setSelectId(flowId);
  selectedInfoStore.setSelectType('flow');
  return { graphData, flowId, flowName };
}

onBeforeUnmount(() => {
  console.warn('LFDemo before unmount, destroy LogicFlow instance');
  // 断开 RightPanel 宽度监听
  if (miniMapResizeObserver) {
    try {
      miniMapResizeObserver.disconnect();
    }
    catch (err) {
      // ignore
    }
    miniMapResizeObserver = null;
  }
  if (lf.value) {
    lf.value.destroy();
  }
});

// function getGraphData() {
//   const flowData = lf.value.getGraphRawData();
//   console.warn('流程图数据getGraphRawData', flowData);
// }

// function renderGraphData() {
//   const graphData = parseBpmnXmlToGraph(xmlData);
//   const flowId = elementsInfoStore.elements.find((pd: any) => pd.type === 'flow')?.id || '';
//   lf.value.graphModel.flowId = flowId;
//   lf.value.render(graphData);
//   lf.value.translateCenter();

//   selectedInfoStore.setSelectId(flowId);
//   selectedInfoStore.setSelectType('flow');
// }

const refFile = ref<HTMLInputElement | null>(null);
async function importLocalFile() {
  const file = refFile.value?.files?.[0];
  const reader = new FileReader();
  reader.readAsText(file!);
  reader.onload = async function (event: any) {
    const xmlStr = event.target.result as string;
    // console.log('importfile', xmlStr);
    await bpmnModeler.value?.importXML(xmlStr);
    const { xml } = await bpmnModeler.value!.saveXML({ format: true });
    const graphData = parseBpmnXmlToGraph(xml as string);
    console.warn('imported graphData', graphData);
    // 更改流程图id和名称
    elementsInfoStore.editFlowId(props.flowId as string);
    elementsInfoStore.editFlowName(props.flowName as string);
    lf.value.render(graphData);
    lf.value.translateCenter();
  };
  // 读取失败的回调
  reader.onerror = function () {
    console.warn(`文件读取失败：${reader.error?.message}`);
  };

  nextTick(() => {
    if (refFile.value) {
      refFile.value.value = ''; // 清空value
    }
  });
}

// 根据所需类型进行转码并返回下载地址
function setEncoded(type: string, data: string) {
  const filename = 'diagram';
  const encodedData = encodeURIComponent(data);
  return {
    filename: `${filename}.${type}`,
    href: `data:application/${
      type === 'svg' ? 'text/xml' : 'bpmn20-xml'
    };charset=UTF-8,${encodedData}`,
    data,
  };
}

async function downloadProcessAsXml() {
  const xmlString = getModelXml();
  const { href, filename } = setEncoded('xml', xmlString);
  if (href && filename) {
    const a = document.createElement('a');
    a.download = filename; // 指定下载的文件名
    a.href = href; //  URL对象
    a.click(); // 模拟点击
    URL.revokeObjectURL(a.href); // 释放URL 对象
  }
}
</script>

<template>
  <div class="page-main logic-panel">
    <div id="bpmnCanvas" style="display: none;" />
    <div ref="container" class="flow-container" />
    <div class="importFile" @click="refFile?.click()">
      <FolderOpenOutlined />
      <div>导入本地文件</div>
      <input ref="refFile" type="file" style="display: none;" accept=".xml, .bpmn" @change="importLocalFile">
    </div>
    <div class="exportXMLFile" @click="downloadProcessAsXml">
      <DownloadOutlined />
      <div>下载xml文件</div>
    </div>
    <RightPanel ref="rightPanelRef" />
    <DrawerViewXml ref="drawerViewXmlRef" />
    <DrawerViewJson ref="drawerViewJsonRef" />
    <NextNodePanel ref="nextNodePanelRef" />
  </div>
</template>

<style scoped>
.page-main {
  position: relative;
  width: 100%;
  height: 100%;
  padding: 4px;
  overflow: hidden;
  background-color: #fff;
}

.importFile {
  position: absolute;
  top: 10px;
  left: 100px;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 54px;
  padding: 0px 6px;
  cursor: pointer;
  background-color: #f9f9f9;
  border: 1px solid #ccc;
  border-radius: 4px;
}

.exportXMLFile {
  position: absolute;
  top: 10px;
  left: 210px;
  z-index: 10;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 54px;
  padding: 0px 6px;
  cursor: pointer;
  background-color: #f9f9f9;
  border: 1px solid #ccc;
  border-radius: 4px;
}
.flow-container {
  width: 100%;
  height: 100%;
}
.node_text {
  font-size: 16px;
  font-weight: 500;
  line-height: 30px;
  color: #333;
  text-align: center;
  background-color: #07e3e7;
}
</style>

<style>
.lf-control {
  right: unset;
  left: 310px;
}
.lf-dndpanel {
  padding: 15px 10px;
  margin-top: 80px;
}
.logic-panel foreignObject {
  overflow: visible !important;
}

.custom-minimap {
  background-image: url("@/assets/images/daohang.png");
}
.custom-clear {
  background-image: url("@/assets/images/clear.png");
}
.custom-xml {
  background-image: url("@/assets/images/xml.png");
}
.custom-json {
  background-image: url("@/assets/images/json.png");
}
.lf-edge-delete-icon {
  cursor: pointer;
}
.opt-hide {
  display: none;
}
.opt-show {
  display: block;
}
</style>
