<template>
  <Dialog v-model="dialogVisible" :title="dialogTitle" width="92%" :fullscreen="false" top="4vh">
    <div class="hiprint-preview">
      <div class="hiprint-toolbar">
        <div class="hiprint-paper">
          <el-button
            v-for="(value, type) in paperTypes"
            :key="type"
            size="small"
            :type="curPaperType === type ? 'primary' : 'default'"
            @click="setPaper(type, value)"
          >
            {{ type }}
          </el-button>
          <el-popover placement="bottom-start" :width="260" trigger="click" v-model:visible="paperPopVisible">
            <template #reference>
              <el-button size="small" :type="curPaperType === 'other' ? 'primary' : 'default'">自定义纸张</el-button>
            </template>
            <div class="paper-pop">
              <div class="paper-pop-title">设置纸张宽高(mm)</div>
              <div class="paper-pop-form">
                <el-input-number v-model="paperWidth" :precision="1" :min="1" controls-position="right" />
                <span>x</span>
                <el-input-number v-model="paperHeight" :precision="1" :min="1" controls-position="right" />
              </div>
              <el-button class="mt-8px" size="small" type="primary" @click="setPaperOther">确定</el-button>
            </div>
          </el-popover>
        </div>
        <div class="hiprint-zoom">
          <el-button size="small" @click="changeScale(false)">
            <Icon icon="ep:zoom-out" />
          </el-button>
          <div class="zoom-value">{{ (scaleValue * 100).toFixed(0) }}%</div>
          <el-button size="small" @click="changeScale(true)">
            <Icon icon="ep:zoom-in" />
          </el-button>
        </div>
        <el-button size="small" @click="rotatePaper">
          <Icon icon="ep:refresh-right" class="mr-4px" />
          旋转
        </el-button>
        <el-button size="small" type="warning" @click="handlePreview">
          <Icon icon="ep:view" class="mr-4px" />
          预览
        </el-button>
        <el-popconfirm title="是否确认清空?" @confirm="clearPaper">
          <template #reference>
            <el-button size="small" type="danger">
              <Icon icon="ep:delete" class="mr-4px" />
              清空
            </el-button>
          </template>
        </el-popconfirm>
        <el-button type="primary" size="small" :loading="saveLoading" @click="handleSave">
          <Icon icon="ep:check" class="mr-4px" />
          {{ t('common.save') }}
        </el-button>
      </div>

      <div class="hiprint-body">
        <div class="hiprint-left">
          <div class="hiprint-title">基础元素</div>
          <div :id="dragContainerId" class="hiprint-drag-wrap">
            <div v-for="item in baseElements" :key="item.tid" class="ep-draggable-item hiprint-item" :tid="item.tid">
              <span>{{ item.label }}</span>
            </div>
          </div>
          <div v-if="barcodeGroups.length" :id="barcodeDragContainerId" class="hiprint-barcode-groups">
            <div v-for="group in barcodeGroups" :key="group.value" class="hiprint-barcode-group">
              <div class="hiprint-title hiprint-title-group">{{ group.label }}</div>
              <div class="hiprint-drag-wrap hiprint-drag-wrap-compact">
                <div v-for="item in group.elements" :key="item.tid" class="ep-draggable-item hiprint-item" :tid="item.tid">
                  <span style="display: block; text-align: center;">{{ item.label }}</span>
                </div>
              </div>
            </div>
          </div>
        </div>
        <div class="hiprint-center">
          <div :id="designerContainerId"></div>
        </div>
        <div class="hiprint-right">
          <div :id="settingContainerId"></div>
          <div v-if="selectedIconElement" class="hiprint-custom-setting">
            <div class="hiprint-title hiprint-title-inline">图标属性</div>
            <el-form label-position="top" size="small">
              <el-form-item label="图标">
                <IconSelect v-model="selectedIconName" :persistent="false" :teleported="false" />
              </el-form-item>
              <el-form-item label="颜色">
                <el-color-picker v-model="selectedIconColor" show-alpha />
              </el-form-item>
            </el-form>
          </div>
        </div>
      </div>
    </div>

    <el-dialog v-model="previewVisible" title="打印预览" width="70%" top="2vh" append-to-body>
      <div v-html="previewHtml" style="background: #fff;"></div>
    </el-dialog>
  </Dialog>
</template>

<script setup lang="ts">
import { defaultElementTypeProvider, hiprint } from 'vue-plugin-hiprint'
import Iconify from '@purge-icons/generated'
import { PrintTemplateApi } from '@/api/mes/printtemplate'
import { getSimpleDictDataList } from '@/api/system/dict/dict.data'
import qrCodeImg from '@/assets/imgs/qrCode.png'

const { t } = useI18n()
const message = useMessage()

const baseElements = [
  { tid: 'defaultModule.text', label: '文本' },
  { tid: 'defaultModule.image', label: '图片' },
  { tid: 'qrcodeModule.qrcode', label: '二维码' },
  { tid: 'iconModule.icon', label: '图标' },
  { tid: 'defaultModule.longText', label: '长文' },
  { tid: 'defaultModule.table', label: '表格' },
  { tid: 'defaultModule.hline', label: '横线' },
  { tid: 'defaultModule.vline', label: '竖线' },
  { tid: 'defaultModule.rect', label: '矩形' },
  { tid: 'defaultModule.oval', label: '圆形' }
]

const defaultIconName = 'ep:star-filled'
const defaultIconColor = '#409EFF'

const buildIconHtml = (iconName?: string, color?: string) => {
  const finalIconName = iconName || defaultIconName
  const finalColor = color || defaultIconColor
  const svg = Iconify.renderSVG(finalIconName, {
    height: '100%',
    width: '100%'
  })
  if (svg) {
    svg.setAttribute('color', finalColor)
    svg.style.color = finalColor
    svg.style.display = 'block'
    svg.style.width = '100%'
    svg.style.height = '100%'
    return svg.outerHTML
  }
  return `<span class="iconify" data-icon="${finalIconName}" data-width="100%" data-height="100%" style="display:inline-block;width:100%;height:100%;color:${finalColor};"></span>`
}

const buildIconFormatter = () => {
  return `function(value, options) {
    const iconName = options.iconName || '${defaultIconName}';
    const color = options.color || '${defaultIconColor}';
    if (window.__hiprintBuildIconHtml) {
      return window.__hiprintBuildIconHtml(iconName, color);
    }
    return '<span class="iconify" data-icon="' + iconName + '" data-width="100%" data-height="100%" style="display:inline-block;width:100%;height:100%;color:' + color + ';"></span>';
  }`
}

const ensureIconRuntime = () => {
  ;(window as any).__hiprintBuildIconHtml = buildIconHtml
}

type BarcodeElementItem = {
  tid: string
  label: string
}

type BarcodeElementGroup = {
  value: string | number
  label: string
  elements: BarcodeElementItem[]
}

const barcodeGroups = ref<BarcodeElementGroup[]>([])
const barcodeDictData = ref<any[]>([])

const loadBarcodeDictData = async () => {
  try {
    const res = await getSimpleDictDataList()
    const filtered = (res || []).filter((item: any) => item.dictType === 'print_template_type')
    barcodeDictData.value = filtered
    barcodeGroups.value = filtered.map((item: any) => ({
      value: item.value,
      label: item.label,
      elements: [
        { tid: `barcodeModule.${item.value}_qrcode`, label: '二维码' },
        { tid: `barcodeModule.${item.value}_name`, label: '名称' },
        { tid: `barcodeModule.${item.value}_code`, label: '编码' }
      ]
    }))
  } catch (e) {
    console.error('加载条码字典数据失败', e)
  }
}

const barcodeProvider = function () {
  const addElementTypes = function (context: any) {
    context.removePrintElementTypes('barcodeModule')
    if (!barcodeDictData.value.length) return
    const groups: any[] = []
    barcodeDictData.value.forEach((item: any) => {
      const remark = item.remark || ''
      const parts = remark.split(',').map((s: string) => s.trim())
      const qrcodeField = parts[0] || ''
      const nameField = parts[1] || ''
      const codeField = parts[2] || parts[1] || ''
      groups.push(
        new hiprint.PrintElementTypeGroup(item.label, [
          {
            tid: `barcodeModule.${item.value}_qrcode`,
            title: '二维码',
            type: 'image',
            options: {
              field: qrcodeField,
              src: qrCodeImg,
              testData: qrCodeImg,
              width: 80,
              height: 80,
              title: '二维码'
            }
          },
          {
            tid: `barcodeModule.${item.value}_name`,
            title: '名称',
            type: 'text',
            options: {
              field: nameField,
              testData: '',
              title: '名称',
              fontSize: 10,
              textAlign: 'center',
              width: 80,
              height: 16
            }
          },
          {
            tid: `barcodeModule.${item.value}_code`,
            title: '编码',
            type: 'text',
            options: {
              field: codeField,
              testData: '',
              title: '编码',
              fontSize: 10,
              textAlign: 'center',
              width: 80,
              height: 16
            }
          }
        ])
      )
    })
    context.addPrintElementTypes('barcodeModule', groups)
  }
  return { addElementTypes }
}

const qrcodeProvider = function () {
  const addElementTypes = function (context: any) {
    context.removePrintElementTypes('qrcodeModule')
    context.addPrintElementTypes('qrcodeModule', [
      new hiprint.PrintElementTypeGroup('二维码', [
        {
          tid: 'qrcodeModule.qrcode',
          title: '二维码',
          type: 'image',
          options: {
            field: '',
            src: qrCodeImg,
            testData: qrCodeImg,
            width: 80,
            height: 80,
            title: '二维码'
          }
        }
      ])
    ])
  }
  return { addElementTypes }
}

const iconProvider = function () {
  const addElementTypes = function (context: any) {
    context.removePrintElementTypes('iconModule')
    context.addPrintElementTypes('iconModule', [
      new hiprint.PrintElementTypeGroup('图标', [
        {
          tid: 'iconModule.icon',
          title: '图标',
          type: 'html',
          options: {
            left: 12,
            top: 12,
            width: 28,
            height: 28,
            iconName: defaultIconName,
            color: defaultIconColor,
            formatter: buildIconFormatter()
          },
          onRendered: function (target: any) {
            const iconify = (window as any).Iconify
            if (iconify?.scan) {
              iconify.scan(target?.[0] || target)
            }
          },
          supportOptions: [
            { name: 'iconName' },
            { name: 'color' },
            { name: 'widthHeight' },
            { name: 'coordinate' },
            { name: 'transform' },
            { name: 'zIndex' }
          ]
        }
      ])
    ])
  }
  return { addElementTypes }
}

const dialogVisible = ref(false)
const dialogTitle = ref('模板配置')
const saveLoading = ref(false)
const currentRow = ref<any>(null)
const currentTemplateJson = ref<Record<string, any> | undefined>(undefined)

const instanceId = `hiprint-designer-${Math.random().toString(36).slice(2)}`
const dragContainerId = `${instanceId}-drag`
const barcodeDragContainerId = `${instanceId}-barcode-drag`
const designerContainerId = `${instanceId}-designer`
const settingContainerId = `${instanceId}-setting`

const paperTypes = {
  A3: { width: 420, height: 296.6 },
  A4: { width: 210, height: 296.6 },
  A5: { width: 210, height: 147.6 },
  B3: { width: 500, height: 352.6 },
  B4: { width: 250, height: 352.6 },
  B5: { width: 250, height: 175.6 }
}
const defaultPaperConfig = {
  type: 'A4',
  width: 210,
  height: 296.6
}
const curPaper = ref({
  ...defaultPaperConfig
})
const paperPopVisible = ref(false)
const paperWidth = ref(220)
const paperHeight = ref(80)
const curPaperType = computed(() => {
  let type = 'other'
  for (const [key, value] of Object.entries(paperTypes)) {
    if (value.width === curPaper.value.width && value.height === curPaper.value.height) {
      type = key
      break
    }
  }
  return type
})

const scaleValue = ref(1)
const scaleMax = 5
const scaleMin = 0.5

const previewVisible = ref(false)
const previewHtml = ref('')
const selectedIconElement = shallowRef<any>(null)
const selectedIconName = ref(defaultIconName)
const selectedIconColor = ref(defaultIconColor)

let hiprintInited = false
let hiprintTemplate: any
let iconSelectEventKey = ''
let iconSelectHandler: ((payload: any) => void) | undefined
let clearSettingHandler: (() => void) | undefined

const normalizePaperConfig = (value: any) => {
  const width = Number(value?.width)
  const height = Number(value?.height)
  if (width <= 0 || height <= 0 || Number.isNaN(width) || Number.isNaN(height)) {
    return { ...defaultPaperConfig }
  }
  return {
    type: typeof value?.type === 'string' && value.type ? value.type : 'other',
    width,
    height
  }
}

const applyPaperConfig = (value: any) => {
  const paper = normalizePaperConfig(value)
  curPaper.value = paper
  paperWidth.value = paper.width
  paperHeight.value = paper.height
}

const getTemplatePaperConfig = (value: any) => {
  const panel = Array.isArray(value?.panels) ? value.panels[0] : undefined
  return panel
}

const ensureInit = () => {
  if (hiprintInited) {
    return
  }
  ensureIconRuntime()
  hiprint.init({
    providers: [new defaultElementTypeProvider(), qrcodeProvider(), barcodeProvider(), iconProvider()]
  })
  hiprintInited = true
}

const isIconPrintElement = (printElement: any) => {
  if (!printElement) {
    return false
  }
  if (printElement?.printElementType?.tid === 'iconModule.icon') {
    return true
  }
  const formatter = String(printElement?.options?.formatter || printElement?.printElementType?.formatter || '')
  return Boolean(
    printElement?.printElementType?.type === 'html'
      && (
        typeof printElement?.options?.iconName === 'string'
        || formatter.includes('__hiprintBuildIconHtml')
      )
  )
}

const normalizeIconPrintElement = (printElement: any) => {
  if (!printElement?.options) {
    return printElement
  }
  if (!printElement.options.iconName) {
    printElement.options.iconName = defaultIconName
  }
  if (!printElement.options.color) {
    printElement.options.color = defaultIconColor
  }
  if (!printElement.options.formatter) {
    printElement.options.formatter = buildIconFormatter()
  }
  return printElement
}

const setSelectedIconState = (printElement: any | null) => {
  const normalizedPrintElement = printElement ? normalizeIconPrintElement(printElement) : null
  selectedIconElement.value = normalizedPrintElement
  selectedIconName.value = normalizedPrintElement?.options?.iconName || defaultIconName
  selectedIconColor.value = normalizedPrintElement?.options?.color || defaultIconColor
}

const updateSelectedIconElement = () => {
  const printElement = selectedIconElement.value
  if (!printElement) {
    return
  }
  printElement.options.iconName = selectedIconName.value || defaultIconName
  printElement.options.color = selectedIconColor.value || defaultIconColor
  printElement.updateDesignViewFromOptions()
  const iconify = (window as any).Iconify
  if (iconify?.scan) {
    iconify.scan(printElement.designTarget?.[0] || printElement.designTarget)
  }
  ;(window as any).hinnn?.event?.trigger(`hiprintTemplateDataChanged_${printElement.templateId}`, '元素修改')
}

const bindIconCustomPanel = (template: any) => {
  const event = (window as any).hinnn?.event
  if (!event) {
    return
  }
  if (iconSelectEventKey && iconSelectHandler) {
    event.off(iconSelectEventKey, iconSelectHandler)
  }
  if (clearSettingHandler) {
    event.off('clearSettingContainer', clearSettingHandler)
  }
  iconSelectEventKey = template?.getPrintElementSelectEventKey?.() || ''
  iconSelectHandler = (payload: any) => {
    const printElement = payload?.printElement
    if (isIconPrintElement(printElement)) {
      setSelectedIconState(printElement)
      return
    }
    setSelectedIconState(null)
  }
  clearSettingHandler = () => {
    setSelectedIconState(null)
  }
  if (iconSelectEventKey) {
    event.on(iconSelectEventKey, iconSelectHandler)
  }
  event.on('clearSettingContainer', clearSettingHandler)
}

const buildLeftElement = () => {
  const jquery = (window as any).$
  if (!jquery) {
    message.warning('未检测到 jQuery，无法加载拖拽元素')
    return
  }
  hiprint.PrintElementTypeManager.buildByHtml(jquery(`#${dragContainerId} .ep-draggable-item`))
  if (barcodeGroups.value.length) {
    hiprint.PrintElementTypeManager.buildByHtml(jquery(`#${barcodeDragContainerId} .ep-draggable-item`))
  }
}

const buildDesigner = () => {
  const jquery = (window as any).$
  if (!jquery) {
    message.warning('未检测到 jQuery，无法初始化打印设计器')
    return
  }
  jquery(`#${designerContainerId}`).empty()
  const template = currentTemplateJson.value || undefined
  hiprintTemplate = new hiprint.PrintTemplate({
    template,
    settingContainer: `#${settingContainerId}`
  })
  bindIconCustomPanel(hiprintTemplate)
  hiprintTemplate.design(`#${designerContainerId}`)
  setPaper(curPaperType.value, { width: curPaper.value.width, height: curPaper.value.height })
  hiprintTemplate.zoom(scaleValue.value)
}

const setPaper = (type: string, value: { width: number; height: number }) => {
  if (!hiprintTemplate) {
    return
  }
  const width = Number(value.width)
  const height = Number(value.height)
  curPaper.value = { type, width, height }
  hiprintTemplate.setPaper(width, height)
}

const setPaperOther = () => {
  paperPopVisible.value = false
  setPaper('other', { width: Number(paperWidth.value), height: Number(paperHeight.value) })
}

const changeScale = (isZoomIn: boolean) => {
  if (!hiprintTemplate) {
    return
  }
  let nextScale = scaleValue.value
  if (isZoomIn) {
    nextScale += 0.1
    if (nextScale > scaleMax) nextScale = scaleMax
  } else {
    nextScale -= 0.1
    if (nextScale < scaleMin) nextScale = scaleMin
  }
  scaleValue.value = nextScale
  hiprintTemplate.zoom(nextScale)
}

const rotatePaper = () => {
  if (!hiprintTemplate) return
  hiprintTemplate.rotatePaper()
}

const handlePreview = () => {
  if (!hiprintTemplate) return
  const jquery = (window as any).$
  const htmlResult = hiprintTemplate.getHtml({})
  if (htmlResult && jquery) {
    previewHtml.value = jquery('<div>').append(htmlResult).html() || ''
  }
  previewVisible.value = true
}

const clearPaper = () => {
  if (!hiprintTemplate) return
  try {
    hiprintTemplate.clear()
    setSelectedIconState(null)
  } catch (error) {
    message.error('清空失败')
  }
}

const handleSave = async () => {
  if (!hiprintTemplate) {
    return
  }
  const templateJson = hiprintTemplate.getJson()
  saveLoading.value = true
  try {
    await PrintTemplateApi.updatePrintTemplate({
      id: currentRow.value.id,
      templateCode: currentRow.value.templateCode,
      templateName: currentRow.value.templateName,
      templateType: currentRow.value.templateType,
      templateBizType: currentRow.value.templateBizType ?? 1,
      templateJson: JSON.stringify(templateJson),
    } as any)
    message.success(t('common.updateSuccess'))
    dialogVisible.value = false
    emit('success')
  } finally {
    saveLoading.value = false
  }
}

const resetState = () => {
  scaleValue.value = 1
  setSelectedIconState(null)
  applyPaperConfig(defaultPaperConfig)
  paperPopVisible.value = false
}

const open = async (row: any) => {
  currentRow.value = row
  dialogTitle.value = `${t('TemplateManagement.PrintTemplate.designTitle')}${row.templateName ? ' - ' + row.templateName : ''}`
  currentTemplateJson.value = row.templateJson ? (typeof row.templateJson === 'string' ? JSON.parse(row.templateJson) : row.templateJson) : undefined
  resetState()
  applyPaperConfig(getTemplatePaperConfig(currentTemplateJson.value))
  await loadBarcodeDictData()
  dialogVisible.value = true
  await nextTick()
  ensureInit()
  buildLeftElement()
  buildDesigner()
}

const emit = defineEmits(['success'])

defineExpose({ open })

watch(selectedIconName, () => {
  updateSelectedIconElement()
})

watch(selectedIconColor, () => {
  updateSelectedIconElement()
})
</script>

<style scoped lang="scss">
.hiprint-preview {
  width: 100%;
}

.hiprint-toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
}

.hiprint-paper {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.hiprint-zoom {
  display: flex;
  align-items: center;
  gap: 4px;
}

.zoom-value {
  width: 56px;
  text-align: center;
  font-size: 13px;
  color: var(--el-text-color-regular);
}

.paper-pop-title {
  font-size: 14px;
  font-weight: 600;
}

.paper-pop-form {
  margin-top: 8px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.hiprint-body {
  height: 75vh;
  display: grid;
  grid-template-columns: 220px 1fr 400px;
  gap: 12px;
}

.hiprint-left,
.hiprint-center,
.hiprint-right {
  background: var(--el-bg-color);
  border: 1px solid var(--el-border-color-light);
  border-radius: 8px;
  overflow: auto;
}

.hiprint-center {
  padding: 16px;
}

.hiprint-right {
  padding: 12px;
}

.hiprint-custom-setting {
  margin-top: 12px;
  border-top: 1px solid var(--el-border-color-light);
  padding-top: 12px;
}

.hiprint-title {
  padding: 10px 10px 0;
  font-weight: 600;
}

.hiprint-title-group {
  padding-top: 12px;
}

.hiprint-title-inline {
  padding: 0 0 12px;
}

.hiprint-barcode-groups {
  padding-bottom: 10px;
}

.hiprint-drag-wrap {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  padding: 10px;
}

.hiprint-drag-wrap-compact {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  padding-top: 8px;
}

.hiprint-item {
  min-height: 56px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
  border: 1px solid var(--el-border-color);
  background: var(--el-fill-color-light);
  color: var(--el-text-color-primary);
  cursor: grab;
}
</style>
