<template>
  <div class="bpmn-designer-container">
    <!-- 暗色模式箭头定义 -->
    <svg width="0" height="0" style="position: absolute">
      <defs>
        <marker id="markerArrow-dark-mode" viewBox="0 0 20 20" refX="11" refY="10" markerWidth="10" markerHeight="10" orient="auto">
          <path d="M 1 5 L 11 10 L 1 15 Z" class="arrow-dark" />
        </marker>
      </defs>
    </svg>

    <div v-loading="loading" class="app-container">
      <el-container class="h-full">
        <el-container style="align-items: stretch">
          <!-- 工具栏 -->
          <el-header height="35px">
            <div class="process-toolbar">
              <el-space wrap :size="10">
                <el-tooltip effect="dark" content="自适应屏幕" placement="bottom">
                  <el-button size="small" icon="Rank" @click="fitViewport" />
                </el-tooltip>
                <el-tooltip effect="dark" content="放大" placement="bottom">
                  <el-button size="small" icon="ZoomIn" @click="zoomViewport(true)" />
                </el-tooltip>
                <el-tooltip effect="dark" content="缩小" placement="bottom">
                  <el-button size="small" icon="ZoomOut" @click="zoomViewport(false)" />
                </el-tooltip>
                <el-tooltip effect="dark" content="后退" placement="bottom">
                  <el-button size="small" icon="Back" @click="undo" />
                </el-tooltip>
                <el-tooltip effect="dark" content="前进" placement="bottom">
                  <el-button size="small" icon="Right" @click="redo" />
                </el-tooltip>
              </el-space>

              <el-space wrap :size="10" style="float: right; padding-right: 10px">
                <el-button size="small" type="primary" @click="saveXml">保 存</el-button>
                <el-dropdown size="small">
                  <el-button size="small" type="primary"> 预 览 </el-button>
                  <template #dropdown>
                    <el-dropdown-menu>
                      <el-dropdown-item icon="Document" @click="previewXML">XML预览</el-dropdown-item>
                      <el-dropdown-item icon="View" @click="previewSVG">SVG预览</el-dropdown-item>
                    </el-dropdown-menu>
                  </template>
                </el-dropdown>
                <el-dropdown size="small">
                  <el-button size="small" type="primary"> 下 载 </el-button>
                  <template #dropdown>
                    <el-dropdown-menu>
                      <el-dropdown-item icon="Download" @click="downloadXML">下载XML</el-dropdown-item>
                      <el-dropdown-item icon="Download" @click="downloadSVG">下载SVG</el-dropdown-item>
                    </el-dropdown-menu>
                  </template>
                </el-dropdown>
              </el-space>
            </div>
          </el-header>

          <!-- 画布 -->
          <div ref="canvasRef" class="canvas" />

          <!-- 属性面板 -->
          <div :class="['process-panel', { 'hide': panelHidden }]">
            <div class="process-panel-bar" @click="togglePanel">
              <el-link type="default" :underline="false">
                <svg-icon class-name="open-bar" :icon-class="panelHidden ? 'caret-back' : 'caret-forward'" />
              </el-link>
            </div>
            <transition enter-active-class="animate__animated animate__fadeIn">
              <div v-if="!panelHidden" class="panel-content">
                <PropertyPanel :modeler="bpmnModeler" />
              </div>
            </transition>
          </div>
        </el-container>
      </el-container>
    </div>

    <!-- XML 预览对话框 -->
    <el-dialog v-model="previewXMLVisible" title="XML预览" width="80%" append-to-body @closed="xmlStr = ''">
      <highlightjs :code="xmlStr" language="xml" />
    </el-dialog>

    <!-- SVG 预览对话框 -->
    <el-dialog v-model="previewSVGVisible" title="SVG预览" width="80%" append-to-body @closed="svgData = ''">
      <div style="text-align: center" v-html="svgData" />
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, nextTick, shallowRef } from 'vue';
import BpmnModeler from 'bpmn-js/lib/Modeler';
import 'bpmn-js/dist/assets/diagram-js.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css';
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css';
import './assets/style/index.scss';

import PropertyPanel from './panel/index.vue';
import defaultXML from './assets/defaultXML';
import flowableModdle from './assets/moddle/flowable';
import Modules from './assets/module/index';
import useModelerStore from '@/store/modules/modeler';
import useDialog from '@/hooks/useDialog';

// ==================== Props & Emits ====================
const emit = defineEmits<{
  (e: 'closeCallBack'): void;
  (
    e: 'saveCallBack',
    data: {
      xml: string;
      svg: string;
      key: string;
      name: string;
      loading: boolean;
    }
  ): void;
}>();

// ==================== Refs ====================
const canvasRef = ref<HTMLDivElement>();
const bpmnModeler = shallowRef<BpmnModeler | null>(null);
const panelHidden = ref(false);
const previewXMLVisible = ref(false);
const previewSVGVisible = ref(false);
const xmlStr = ref('');
const svgData = ref('');
const loading = ref(false);

// ==================== Store & Dialog ====================
const modelerStore = useModelerStore();
const { openDialog, closeDialog } = useDialog({ title: '编辑流程' });

// ==================== 初始化 ====================
const initCanvas = () => {
  if (!canvasRef.value) return;

  bpmnModeler.value = new BpmnModeler({
    container: canvasRef.value,
    keyboard: { bindTo: window },
    propertiesPanel: { parent: undefined }, // 不绑定，由 PropertyPanel 手动管理
    additionalModules: Modules,
    moddleExtensions: { flowable: flowableModdle }
  });

  modelerStore.setModeler(bpmnModeler.value);
};

const initDiagram = async (xml: string = defaultXML) => {
  try {
    await bpmnModeler.value?.importXML(xml);
  } catch (err) {

    // 可 emit 错误事件
  }
};

onMounted(async () => {
  await nextTick();
  initCanvas();
  await initDiagram();
});

// ==================== 工具方法 ====================
const getCommandStack = () => bpmnModeler.value?.get('commandStack');
const getCanvas = () => bpmnModeler.value?.get('canvas');

const undo = () => getCommandStack()?.undo();
const redo = () => getCommandStack()?.redo();

const fitViewport = () => {
  const canvas = getCanvas();
  if (!canvas) return;

  canvas.zoom('fit-viewport');
  const viewport = document.querySelector<SVGGElement>('.app-container .viewport');
  if (!viewport) return;

  const bbox = viewport.getBBox();
  const currentViewbox = canvas.viewbox();
  const center = { x: bbox.x + bbox.width / 2, y: bbox.y + bbox.height / 2 };

  canvas.viewbox({
    x: center.x - currentViewbox.width / 2,
    y: center.y - currentViewbox.height / 2,
    width: currentViewbox.width,
    height: currentViewbox.height
  });
  // 可选：缩放系数调整
};

const zoomViewport = (zoomIn: boolean) => {
  const canvas = getCanvas();
  if (!canvas) return;

  const newZoom = canvas.zoom() + (zoomIn ? 0.1 : -0.1);
  canvas.zoom(newZoom);
};

// ==================== 下载与预览 ====================
const saveXML = async () => {
  return await bpmnModeler.value?.saveXML({ format: true });
};

const saveSVG = async () => {
  return await bpmnModeler.value?.saveSVG();
};

const downloadFile = (filename: string, content: string, mimeType: string) => {
  try {
    const blob = new Blob([content], { type: mimeType });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    a.click();
    URL.revokeObjectURL(url);
  } catch (error) {

  }
};

const downloadXML = async () => {
  try {
    const result = await saveXML();
    if (result?.xml) {
      const name = getProcessName() || 'process';
      downloadFile(`${name}.bpmn20.xml`, result.xml, 'application/xml');
    }
  } catch (e) {

  }
};

const downloadSVG = async () => {
  try {
    const result = await saveSVG();
    if (result?.svg) {
      const name = getProcessName() || 'process';
      downloadFile(`${name}.svg`, result.svg, 'image/svg+xml');
    }
  } catch (e) {

  }
};

const previewXML = async () => {
  try {
    const result = await saveXML();
    if (result?.xml) {
      xmlStr.value = result.xml;
      previewXMLVisible.value = true;
    }
  } catch (e) {

  }
};

const previewSVG = async () => {
  try {
    const result = await saveSVG();
    if (result?.svg) {
      svgData.value = result.svg;
      previewSVGVisible.value = true;
    }
  } catch (e) {

  }
};

// ==================== 获取流程信息 ====================
const getProcessElement = () => {
  const definitions = bpmnModeler.value?.getDefinitions();
  return definitions?.rootElements?.find((el: any) => el.$type === 'bpmn:Process');
};

const getProcessName = () => {
  return getProcessElement()?.name || '未命名流程';
};

const getProcessId = () => {
  return getProcessElement()?.id || 'undefined';
};

// ==================== 保存 ====================
const saveXml = async () => {
  try {
    loading.value = true;
    const { xml } = await saveXML();
    const { svg } = await saveSVG();

    emit('saveCallBack', {
      xml,
      svg,
      key: getProcessId(),
      name: getProcessName(),
      loading: loading.value
    });
  } catch (e) {

  } finally {
    loading.value = false;
  }
};

// ==================== 面板控制 ====================
const togglePanel = () => {
  panelHidden.value = !panelHidden.value;
};

// ==================== 对外暴露 API ====================
defineExpose({
  initDiagram,
  saveXml,
  openDialog,
  closeDialog
});
</script>

<!-- ✅ 使用 scoped 避免全局污染 -->
<style scoped>
.bpmn-designer-container {
  height: 100%;
}

.app-container {
  width: 100%;
  height: 100%;
}

.canvas {
  width: 100%;
  height: 100%;
}

.process-toolbar {
  height: 35px;
  padding: 5px 10px;
  background-color: var(--el-bg-color);
  border-bottom: 1px solid var(--bpmn-panel-border);
}

.open-bar {
  font-size: 20px;
  cursor: pointer;
  text-align: center;
}
</style>

<!-- ✅ 仅对 bpmn-js 内部结构样式做穿透 -->
<style lang="scss">
// 面板样式（可抽离为独立 SCSS 文件）
.process-panel {
  transition: width 0.25s ease-in;
  box-sizing: border-box;
  padding: 0 8px;
  border-left: 1px solid var(--bpmn-panel-border);
  box-shadow: var(--bpmn-panel-box-shadow) 0 0 8px;
  max-height: 100%;
  width: 25%;
  height: calc(100vh - 100px);

  &.hide {
    width: 34px;
    overflow: hidden;
    padding: 0;

    .process-panel-bar {
      width: 34px;
      height: 100%;
      display: block;
      text-align: left;
      line-height: 34px;
    }

    &:hover {
      background-color: var(--bpmn-panel-bar-background-color);
    }
  }
}

.process-panel-bar {
  width: 34px;
  height: 40px;
  line-height: 40px;
  text-align: center;
}

.panel-content {
  height: calc(100vh - 182px);
  overflow: auto;
}

// 代码高亮
pre {
  margin: 0;
  height: 100%;
  max-height: calc(80vh - 32px);
  overflow: auto;

  .hljs {
    word-break: break-word;
    white-space: pre-wrap;
    padding: 0.5em;
  }
}
</style>

<!-- ✅ 主题相关样式（可独立为 theme-dark.scss） -->
<style lang="scss">
// 暗色模式主题
html[class='dark'] {
  $stroke-color-dark: white;
  $bpmn-font-size: 12px;
  $bpmn-font-color-dark: white;

  .arrow-dark {
    stroke: $stroke-color-dark;
    fill: $stroke-color-dark;
    stroke-width: 1px;
    stroke-linecap: round;
    stroke-linejoin: round;
  }

  svg.new-parent {
    background-color: black !important;
    background-image: linear-gradient(90deg, hsla(0, 0%, 78.4%, 0.15) 10%, transparent 0),
      linear-gradient(hsla(0, 0%, 78.4%, 0.15) 10%, transparent 0) !important;
    background-size: 10px 10px !important;
  }

  div.djs-direct-editing-parent {
    color: $bpmn-font-color-dark;
    background-color: transparent !important;
    border-radius: 10px;
  }

  g.djs-visual {
    > *:first-child:not(.djs-label) {
      stroke: $stroke-color-dark !important;
    }

    .djs-label {
      fill: $bpmn-font-color-dark !important;
      font-size: $bpmn-font-size !important;
    }

    path[data-corner-radius] {
      stroke: $stroke-color-dark !important;
      marker-end: url('#markerArrow-dark-mode') !important;
    }
  }
}
</style>
