<template>
  <div :class="['flow-containers', { 'view-mode': isView }]">
    <a-spin :spinning="isView">
      <a-layout style="height: 100%">
        <a-layout-header style="border-bottom: 1px solid #dadada; height: auto; padding: 0">
          <div style="display: flex; padding: 0px 0; justify-content: space-between; align-items: center;">
            <a-space>
              <a-upload :before-upload="openBpmn" :show-upload-list="false">
                <a-tooltip title="加载xml" placement="bottom">
                  <a-button size="small" style="margin-right: 10px;">
                    <FolderOpenOutlined />
                  </a-button>
                </a-tooltip>
              </a-upload>
              <a-tooltip title="新建" placement="bottom">
                <a-button size="small" @click="newDiagram">
                  <PlusCircleOutlined />
                </a-button>
              </a-tooltip>
              <a-tooltip title="自适应屏幕" placement="bottom">
                <a-button size="small" @click="fitViewport">
                 <FullscreenOutlined />
                </a-button>
              </a-tooltip>
              <a-tooltip title="放大" placement="bottom">
                <a-button size="small" @click="zoomViewport(true)">
                  <ZoomInOutlined />
                </a-button>
              </a-tooltip>
              <a-tooltip title="缩小" placement="bottom">
                <a-button size="small" @click="zoomViewport(false)">
                 <ZoomOutOutlined />
                </a-button>
              </a-tooltip>
              <a-tooltip title="后退" placement="bottom">
                <a-button size="small" @click="undo">
                  <RollbackOutlined />
                </a-button>
              </a-tooltip>
              <a-tooltip title="前进" placement="bottom">
                <a-button size="small" @click="redo">
                 <ForwardOutlined />
                </a-button>
              </a-tooltip>

              <a-tooltip title="全屏" placement="bottom">
                <a-button size="small" @click="toggleFullscreen">
                 <FullscreenOutlined />
                </a-button>
              </a-tooltip>
            </a-space>
            
            <a-space>
              <a-button size="small" @click="showXML">
               <EyeOutlined />查看xml
              </a-button>
              <a-button size="small" @click="() => saveXML(true)">
                <DownloadOutlined />
                下载xml
              </a-button>
              <a-button size="small" @click="() => saveImg('svg', true)">
                <PictureOutlined />
                下载svg
              </a-button>
              <a-button size="small" type="primary" @click="save">
               <SaveOutlined />
                保存模型
              </a-button>
              <a-button size="small" type="primary" @click="handleGoback">
               <CloseOutlined />
               关闭
              </a-button>
            </a-space>
          </div>
        </a-layout-header>
        
        <!-- 流程设计页面 -->
        <a-layout-content style="height: calc(100vh - 64px); overflow: hidden;">
          <a-layout style="height: 100%;">
            <a-layout-content style="position: relative;">
              <div ref="canvas" class="canvas"/>
              <div v-if="!loadCanvas" class="canvas-loading">
                <a-spin tip="画布加载中..." />
              </div>
            </a-layout-content>

            <a-layout-sider :width="500">
              <a-card :bordered="false" class="normalPanel" :body-style="{padding: '0px', height: '100%' }">
                <Designer v-if="loadCanvas" />
              </a-card>
            </a-layout-sider>
            
          </a-layout>
        </a-layout-content>
      </a-layout>
    </a-spin>
  </div>
</template>

<script lang="ts" setup>
import { ref, computed, watch, onMounted, reactive, onUnmounted, nextTick } from 'vue'
import { 
  FolderOpenOutlined,
  PlusCircleOutlined,
  FullscreenOutlined,
  ZoomInOutlined,
  ZoomOutOutlined,
  RollbackOutlined,
  ForwardOutlined,
  EyeOutlined,
  DownloadOutlined,
  PictureOutlined,
  SaveOutlined,
  CloseOutlined
} from '@ant-design/icons-vue'

import { message } from 'ant-design-vue'
import Modeler from 'bpmn-js/lib/Modeler'
import Designer from './designer.vue'
import { getInitStr }  from './common/init'
import { StrUtil } from './common/StrUtil'
// 引入flowable的节点文件
import FlowableModule from './common/flowable.json'
import customControlsModule from './customPanel'
import customTranslate from './customPanel/customTranslate'
import { useModelerStore } from './store/modeler-store.js'

// 组件传值
interface Props {
  xml?: string
  isView?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  xml: '',
  isView: false
})

const emit = defineEmits(['showXML', 'save', "goback"])

const isFullscreen = ref(false)

// 数据定义
const modeler = ref<Modeler | null>(null)
const zoom = ref(1)
const loadCanvas = ref(false)
const canvas = ref<HTMLElement | null>(null)

const modelerStore = useModelerStore()

// 计算属性
const additionalModules = computed(() => {
  const Modules = []
  Modules.push(customControlsModule)
  Modules.push({
    translate: ['value', customTranslate]
  })
  return Modules
})

// 方法定义
// 根据默认文件初始化流程图
const newDiagram = () => {
  createNewDiagram(getInitStr())
}

// 根据提供的xml创建流程图 - 修复后的版本
const createNewDiagram = async (data: string) => {
  if (!modelerStore.modeler) return
  
  // 修复 XML 处理
  data = data.replace(/<!\[CDATA\[(.+?)]]>/g, function (match, str) {
    return str.replace(/</g, '&lt;')
  })
  
  try {
    await modelerStore.modeler.importXML(data)
    
    // 使用延迟和安全的视图适应
    await nextTick()
    setTimeout(() => {
      safeFitViewport()
    }, 300) // 增加延迟时间确保完全渲染
  } catch (err) {
    console.error('流程图加载失败:', err)
    message.error('流程图加载失败')
  }
}

// 完全避免使用 viewbox 方法的安全适应方案
const safeFitViewport = () => {
  if (!modelerStore.canvas) return
  
  try {
    // 方案1: 直接使用 zoom 方法，避免 viewbox 操作
    console.log('尝试方案1: 直接使用 fit-viewport')
    modelerStore.canvas.zoom('fit-viewport', 'auto')
    
    // 延迟设置加载完成状态
    setTimeout(() => {
      zoom.value = modelerStore.canvas?.zoom() || 1
      loadCanvas.value = true
      console.log('视图适应完成')
    }, 100)
    
  } catch (error) {
    console.warn('方案1失败，尝试方案2:', error)
    
    // 方案2: 使用更保守的缩放
    try {
      modelerStore.canvas.zoom(0.8)
      setTimeout(() => {
        loadCanvas.value = true
      }, 50)
    } catch (e) {
      console.error('所有视图适应方案都失败了:', e)
      // 最终方案: 直接设置加载完成，让用户手动调整
      loadCanvas.value = true
      message.warning('画布加载完成，如需调整视图请手动缩放')
    }
  }
}

// 原有的 fitViewport 方法
const fitViewport = () => {
  safeFitViewport()
}

// 放大缩小 - 使用安全的缩放方法
const zoomViewport = (zoomIn = true) => {
  if (!modelerStore.canvas) return
  
  try {
    const currentZoom = modelerStore.canvas.zoom()
    const newZoom = currentZoom + (zoomIn ? 0.1 : -0.1)
    
    // 限制缩放范围
    const clampedZoom = Math.max(0.2, Math.min(3, newZoom))
    modelerStore.canvas.zoom(clampedZoom)
    zoom.value = clampedZoom
  } catch (error) {
    console.warn('缩放操作失败:', error)
  }
}

// 后退操作
const undo = () => {
  if (modelerStore.modeler) {
    try {
      const commandStack = modelerStore.modeler.get('commandStack')
      if (commandStack.canUndo()) {
        commandStack.undo()
      } else {
        message.info('无法后退')
      }
    } catch (error) {
      console.error('后退操作失败:', error)
      message.error('后退操作失败')
    }
  }
}

// 前进操作
const redo = () => {
  if (modelerStore.modeler) {
    try {
      const commandStack = modelerStore.modeler.get('commandStack')
      if (commandStack.canRedo()) {
        commandStack.redo()
      } else {
        message.info('无法前进')
      }
    } catch (error) {
      console.error('前进操作失败:', error)
      message.error('前进操作失败')
    }
  }
}

// 获取流程基础信息
const getProcess = () => {
  const element = getProcessElement()
  if (!element) return {}
  
  return {
    id: element.id,
    name: element.name,
    category: element.processCategory
  }
}

// 获取流程主面板节点
const getProcessElement = () => {
  if (!modelerStore.modeler) return null
  
  const rootElements = modelerStore.modeler.getDefinitions().rootElements
  for (let i = 0; i < rootElements.length; i++) {
    if (rootElements[i].$type === 'bpmn:Process') return rootElements[i]
  }
  return null
}

// 保存xml
const saveXML = async (download = false) => {
  if (!modelerStore.modeler) return
  
  try {
    const { xml } = await modelerStore.modeler.saveXML({ format: true })
    if (download) {
      const processElement = getProcessElement()
      const filename = processElement?.name ? `${processElement.name}.bpmn20.xml` : 'process.bpmn20.xml'
      downloadFile(filename, xml, 'application/xml')
    }
    return xml
  } catch (err) {
    console.log(err)
    message.error('保存XML失败')
  }
}

// 在线查看xml
const showXML = async () => {
  try {
    const xmlStr = await saveXML()
    if (xmlStr) {
      emit('showXML', xmlStr)
    }
  } catch (err) {
    console.log(err)
  }
}

// 保存流程图为svg
const saveImg = async (type = 'svg', download = false) => {
  if (!modelerStore.modeler) return
  
  try {
    const { svg } = await modelerStore.modeler.saveSVG({ format: true })
    if (download) {
      const processElement = getProcessElement()
      const filename = processElement?.name || 'process'
      downloadFile(filename, svg, 'image/svg+xml')
    }
    return svg
  } catch (err) {
    console.log(err)
    message.error('保存SVG失败')
  }
}

// 保存流程图
const save = async () => {
  const process = getProcess()
  const xml = await saveXML()
  const svg = await saveImg()
  
  if (xml && svg) {
    const result = { process, xml, svg }
    emit('save', result)
  }
}

// 打开流程文件
const openBpmn = (file: File) => {
  const reader = new FileReader()
  reader.readAsText(file, 'utf-8')
  reader.onload = () => {
    if (typeof reader.result === 'string') {
      createNewDiagram(reader.result)
    }
  }
  return false
}

// 下载流程文件
const downloadFile = (filename: string, data: string, type: string) => {
  const aobj = document.createElement('a')
  const url = window.URL.createObjectURL(new Blob([data], { type }))
  aobj.href = url
  aobj.download = filename
  aobj.click()
  window.URL.revokeObjectURL(url)
}

/** 关闭当前标签页并返回上个页面 */
const handleGoback = async () => {
  emit('goback')
}

// 全屏切换方法
const toggleFullscreen = () => {
  const container = document.querySelector('.flow-containers') as HTMLElement
  
  if (!document.fullscreenElement) {
    // 进入全屏
    if (container.requestFullscreen) {
      container.requestFullscreen().catch(err => {
        console.error('全屏请求失败:', err)
        message.error('全屏模式不可用')
      })
    }
  } else {
    // 退出全屏
    if (document.exitFullscreen) {
      document.exitFullscreen()
    }
  }
}

// 监听全屏状态变化
const handleFullscreenChange = () => {
  isFullscreen.value = !!document.fullscreenElement
}

// 监听xml变化
watch(() => props.xml, (newVal) => {
  if (StrUtil.isNotBlank(newVal)) {
    createNewDiagram(newVal)
  } else {
    newDiagram()
  }
}, { immediate: true })

// 生命周期
onMounted(() => {
  if (!canvas.value) return
  
  /** 创建bpmn 实例 - 修复键盘绑定问题 */
  const modelerInstance = new Modeler({
    container: canvas.value,
    additionalModules: additionalModules.value,
    moddleExtensions: {
      flowable: FlowableModule
    }
    // 移除过时的 keyboard 配置: keyboard: { bindTo: document }
  })
  
  modeler.value = modelerInstance
  // 注册 modeler 相关信息到 store
  modelerStore.setModeler(modelerInstance)
  
  // 直接点击新建按钮时,进行新增流程图
  if (StrUtil.isBlank(props.xml)) {
    newDiagram()
  } else {
    createNewDiagram(props.xml)
  }

  document.addEventListener('fullscreenchange', handleFullscreenChange)
})

onUnmounted(() => {
  document.removeEventListener('fullscreenchange', handleFullscreenChange)
})
</script>

<style lang="scss">
/*左边工具栏以及编辑节点的样式*/
@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";

// 全局样式调整
html, body, #app {
  height: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
}

.view-mode {
  .ant-layout-header, .ant-layout-sider, .djs-palette, .bjs-powered-by {
    display: none;
  }
  
  .ant-spin-container::after {
    background-color: initial;
  }
  
  .ant-spin-text {
    display: none;
  }
}

.flow-containers {
  width: 100%;
  height: 100vh;
  display: flex;
  flex-direction: column;
  
  .full-height-layout {
    height: 100%;
    flex: 1;
    display: flex;
    flex-direction: column;
  }
  
  .layout-header {
    border-bottom: 1px solid rgb(218 218 218);
    height: auto;
    padding-left: 0;
    flex-shrink: 0;
  }
  
  .header-content {
    display: flex;
    padding: 10px 0;
    justify-content: space-between;
    align-items: center;
  }
  
  .main-layout {
    flex: 1;
    display: flex;
    align-items: stretch;
    overflow: hidden;
  }
  
  .canvas-content {
    position: relative;
    padding: 0;
    flex: 1;
    overflow: hidden;
  }
  
  .canvas {
    width: 100%;
    height: 100%;
    min-height: 500px;
    background: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+");
  }
  
  .canvas-loading {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 10;
  }
  
  .panel {
    position: absolute;
    right: 0;
    top: 50px;
    width: 300px;
  }
  
  .load {
    margin-right: 10px;
  }
  
  .normalPanel {
    width: 460px;
    height: 100%;
    padding: 20px;
    flex-shrink: 0;
    overflow-y: auto;
  }
  
  .ant-layout-content .button-group {
    display: flex;
    flex-direction: column;
    position: absolute;
    width: auto;
    height: auto;
    top: 10px;
    right: 10px;
  }
  
  .button-group .ant-btn {
    width: 100%;
    margin: 0 0 5px;
  }
}
</style>