<template>
  <template v-if="!readonly">
    <div
      class="relative flex size-full flex-col rounded-xl bg-[#f2f4f7] px-3 py-2 shadow"
    >
      <div
        class="flex items-center gap-2 py-1 text-xs font-semibold text-gray-700"
      >
        <div class="flex items-center">
          <slot name="title">
            {{ title }}
          </slot>
        </div>
        <div class="ml-auto">{{ state.textCount }}</div>
        <varsPopover :node="node" @setVars="setVars">
          <el-tooltip content="变量" placement="top">
            <SvgIcon name="vars" color="#364153" size="16" />
          </el-tooltip>
        </varsPopover>
        <el-tooltip content="复制" placement="top">
          <SvgIcon
            class="__hover"
            color="#364153"
            name="copy"
            size="16"
            @click="onCopy(ref_textarea.innerText)"
          />
        </el-tooltip>
        <template v-if="delFunc">
          <el-tooltip content="删除" placement="top">
            <SvgIcon
              class="__hover"
              color="#364153"
              name="czr_del"
              size="16"
              @click="delFunc?.()"
            />
          </el-tooltip>
        </template>
      </div>
      <div class="text-text-secondary flex-1 overflow-y-auto py-1 text-[13px]">
        <div
          class="break-all"
          style="line-height: 1.5"
          ref="ref_textarea"
          contenteditable="true"
          @input="handleInput"
          @paste="handlePaste"
          @focus="state.isFocus = true"
          @blur="state.isFocus = false"
        ></div>
      </div>
      <Teleport to="body">
        <div
          class="absolute w-[300px] bg-white shadow"
          :variable-list="true"
          :style="state.variableListStyle"
          v-if="state.showVariableList"
        >
          <div class="filter">
            <el-input
              v-model="state.text"
              :prefix-icon="Search"
              placeholder="搜索变量"
              clearable
            />
          </div>
          <div class="flex flex-col gap-1.5 p-1">
            <template v-for="item in optionsCpt">
              <div class="flex flex-col" v-if="item.options?.length > 0">
                <div class="mb-1 px-2 text-sm text-gray-400">
                  {{ item.label }}
                </div>
                <template v-for="son in item.options">
                  <div class="__hover-bg px-2 py-1" @click="setVars(son)">
                    <varsItem :item="son" />
                  </div>
                </template>
              </div>
            </template>
          </div>
        </div>
      </Teleport>
    </div>
  </template>
  <template v-else>
    <div
      class="flex size-full flex-col overflow-hidden rounded-sm bg-[#f2f4f7] px-1 shadow"
    >
      <div
        class="flex items-center gap-2 py-1 text-xs font-semibold text-gray-700"
      >
        <div>{{ title }}</div>
      </div>
      <div class="text-text-secondary flex-1 py-0 text-xs">
        <div
          class="pb-1 break-all"
          style="line-height: 1.2"
          ref="ref_textarea"
          @input="handleInput"
        />
      </div>
    </div>
  </template>
</template>

<script setup lang="ts">
import {
  ref,
  computed,
  onMounted,
  reactive,
  h,
  render,
  createApp,
  watch,
  nextTick,
  onUnmounted,
} from 'vue'
import { copy, domRootHasAttr } from '@/utils/czr-util'
import { ElMessage } from 'element-plus'
import SvgIcon from '@/components/SvgIcon/index.vue'
import varsPopover from '@/views/workflow/instance/component/vars/vars-popover.vue'
import paramValue from '@/views/workflow/instance/component/params-textarea/param-value.vue'
import { useWorkflowStore } from '@/stores'
import { Search } from '@element-plus/icons-vue'
import varsItem from '@/views/workflow/instance/component/vars/vars-item.vue'

const WorkflowStore = useWorkflowStore()
const emit = defineEmits(['update:modelValue'])
const props = defineProps({
  modelValue: {
    type: String,
    default: '',
  },
  node: {},
  title: {},
  readonly: {
    default: false,
  },
  delFunc: { default: undefined },
})
const state = reactive({
  textCount: 0,
  optionsMap: new Map(),
  options: [],
  lastSelection: null,
  isFocus: false,
  lastSlashPosition: null,
  variableListStyle: {},
  showVariableList: false,
  text: '',
})
const ref_textarea = ref()
watch(
  () => props.modelValue,
  (n) => {
    if (props.readonly) {
      ref_textarea.value.innerHTML = n.replace(
        /\{\{#([^#]+)#\}\}/g,
        (match, p1) => {
          const k = [
            p1.substring(0, p1.indexOf('.')),
            p1.substring(p1.indexOf('.') + 1),
          ]
          const vars = state.optionsMap.get(`${k[0]}.${k[1]}`)
          const dom = document.createElement('div')
          dom.appendChild(initVarsDom(vars))
          return dom.innerHTML
        },
      )
    }
  },
)

watch(
  () => props.node,
  (n) => {
    if (n) {
      const all = WorkflowStore.getInVars(n)
      const map = new Map()
      all.forEach((p) => {
        p.options.forEach((v) => {
          map.set(`${v.nodeId}.${v.key}`, v)
        })
      })
      state.optionsMap = map
      state.options = all
    }
  },
  { immediate: true },
)

const handleInput = (e) => {
  if (e.target.innerText) {
    const brTags = e.target.querySelectorAll('br')
    brTags.forEach((br) => {
      br.parentNode.removeChild(br)
    })
  }
  let lastReplacedSpan: any = null

  const regex = /\{\{#([^#]+)#\}\}/g
  const walker = document.createTreeWalker(e.target, NodeFilter.SHOW_TEXT, null)

  while (walker.nextNode()) {
    const node: any = walker.currentNode
    const text: any = node.nodeValue

    if (regex.test(text)) {
      regex.lastIndex = 0
      const fragment = document.createDocumentFragment()
      let lastIndex = 0
      let match

      while ((match = regex.exec(text)) !== null) {
        if (match.index > lastIndex) {
          fragment.appendChild(
            document.createTextNode(text.substring(lastIndex, match.index)),
          )
        }
        const key = match[1]
        const varsValue: any = initVarsDom(state.optionsMap.get(key))
        fragment.appendChild(varsValue)

        lastReplacedSpan = varsValue
        lastIndex = regex.lastIndex
      }

      if (lastIndex < text.length) {
        fragment.appendChild(document.createTextNode(text.substring(lastIndex)))
      }

      node.parentNode.replaceChild(fragment, node)
    }
  }
  if (lastReplacedSpan) {
    const newRange = document.createRange()
    newRange.setStartAfter(lastReplacedSpan)
    newRange.collapse(true)
    const newSelection: any = window.getSelection()
    newSelection.removeAllRanges()
    newSelection.addRange(newRange)
  }
  state.textCount = e.target.innerText?.trim().length || 0
  // isSlashBeforeCursorEnhanced()
  emitValue()
}
function isSlashBeforeCursorEnhanced() {
  const selection = window.getSelection()
  if (selection.rangeCount === 0) return false

  const range = selection.getRangeAt(0)
  let node = range.startContainer
  let offset = range.startOffset

  // 如果当前节点不是文本节点，尝试找到前一个文本节点
  if (node.nodeType !== Node.TEXT_NODE) {
    // 如果光标在元素开始处，需要查找前一个兄弟节点
    if (offset === 0) {
      let prevNode = getPreviousTextNode(node)
      if (!prevNode) return false
      node = prevNode
      offset = node.textContent.length
    } else {
      // 光标在元素中间，需要检查子节点
      const child = node.childNodes[offset - 1]
      const lastTextNode = getLastTextNode(child)
      if (!lastTextNode) return false
      node = lastTextNode
      offset = node.textContent.length
    }
  }

  // 检查前一个字符是否是/
  if (offset > 0) {
    if (node.textContent[offset - 1] === '/') {
      showVariablePopup(range)
      return
    }
  }

  // 如果当前文本节点开头，需要检查前一个文本节点
  const prevTextNode = getPreviousTextNode(node)
  if (prevTextNode && prevTextNode.textContent.length > 0) {
    if (prevTextNode.textContent[prevTextNode.textContent.length - 1] === '/') {
      showVariablePopup(range)
      return
    }
  }

  state.showVariableList = false
}
// 辅助函数：获取前一个文本节点
function getPreviousTextNode(node) {
  let sibling = node.previousSibling
  while (sibling) {
    if (
      sibling.nodeType === Node.TEXT_NODE &&
      sibling.textContent.trim() !== ''
    ) {
      return sibling
    }
    if (
      sibling.nodeType === Node.ELEMENT_NODE &&
      sibling.childNodes.length > 0
    ) {
      const lastChild = getLastTextNode(sibling)
      if (lastChild) return lastChild
    }
    sibling = sibling.previousSibling
  }
  return node.parentNode ? getPreviousTextNode(node.parentNode) : null
}

// 辅助函数：获取最后一个文本节点
function getLastTextNode(node) {
  if (node.nodeType === Node.TEXT_NODE) return node
  if (node.nodeType === Node.ELEMENT_NODE && node.childNodes.length > 0) {
    for (let i = node.childNodes.length - 1; i >= 0; i--) {
      const result = getLastTextNode(node.childNodes[i])
      if (result) return result
    }
  }
  return null
}
const handlePaste = (e) => {
  e.preventDefault()
  const text = (e.clipboardData || window.clipboardData).getData('text')
  document.execCommand('insertText', false, text)
}
const onCopy = (text) => {
  copy(text)
  ElMessage.success('复制成功！')
}
const initVarsDom = (vars) => {
  const dom = document.createElement('div')
  if (vars) {
    dom.setAttribute('contenteditable', 'false')
    dom.setAttribute('sign', `{{#${vars.nodeId}.${vars.key}#}}`)
    dom.className = 'vars-dom inline-block'
    const app = createApp(paramValue, {
      vars,
    })
    app.component('SvgIcon', SvgIcon)
    app.mount(dom)
  }

  return dom
}
const setVars = (vars) => {
  const nodeToInsert: any = initVarsDom(vars)
  // 检查是否有有效的选择范围且在当前容器内
  if (state.lastSelection) {
    if (state.showVariableList) {
      const range = state.lastSelection.cloneRange()
      const startContainer = range.startContainer
      const startOffset = range.startOffset

      if (startContainer.nodeType === Node.TEXT_NODE && startOffset > 0) {
        const textBefore = startContainer.textContent.slice(0, startOffset)
        if (textBefore.endsWith('/')) {
          // Create a new range covering just the '/' character
          const deleteRange = document.createRange()
          deleteRange.setStart(startContainer, startOffset - 1)
          deleteRange.setEnd(startContainer, startOffset)
          deleteRange.deleteContents()

          // Adjust the original selection position
          range.setStart(startContainer, startOffset - 1)
        }
      }
    }
    // 恢复选区
    const sel: any = window.getSelection()
    sel.removeAllRanges()
    sel.addRange(state.lastSelection)

    // 插入内容
    state.lastSelection.deleteContents()
    state.lastSelection.insertNode(nodeToInsert)
    state.lastSelection.setStartAfter(state.lastSelection.endContainer)
    state.lastSelection.collapse(true)
    state.lastSelection = null
  } else {
    ref_textarea.value.appendChild(nodeToInsert)
  }
  // 可选：滚动到插入的元素
  nodeToInsert.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
  emitValue()
  state.text = ''
  state.showVariableList = false
}
const handleSelectionchange = () => {
  if (state.isFocus) {
    const sel: any = window.getSelection()
    if (sel.rangeCount > 0) {
      state.lastSelection = sel.getRangeAt(0).cloneRange()
    }
  }
}
const emitValue = () => {
  if (!props.readonly) {
    const newDom = document.createElement('div')
    newDom.innerHTML = ref_textarea.value.innerHTML
    const varsDomDivs = newDom.querySelectorAll('div[class*="vars-dom"]')
    // 遍历每个匹配的元素
    varsDomDivs.forEach((div: any) => {
      // 获取sign属性的值
      const signValue = div.getAttribute('sign')
      // 如果sign值存在，就用它替换整个div
      if (signValue) {
        // 创建一个文本节点来替换原div
        const textNode = document.createTextNode(signValue)
        // 用文本节点替换原div
        div.parentNode.replaceChild(textNode, div)
      }
    })
    emit('update:modelValue', newDom.innerHTML)
  }
}

// 显示变量弹窗
const showVariablePopup = (range) => {
  const rect = range.getBoundingClientRect()
  state.variableListStyle = {
    position: 'absolute',
    left: `${rect.left + window.scrollX}px`,
    top: `${rect.bottom + window.scrollY}px`,
    zIndex: 1000,
  }
  state.showVariableList = true
}
const onMouseDown = (e) => {
  if (!domRootHasAttr(e.target, 'variable-list')) {
    state.showVariableList = false
  }
}
const optionsCpt = computed(() => {
  if (!state.text) {
    return state.options
  }
  return state.options
    .map((v: any) => {
      const obj = { ...v }
      obj.options = obj.options.filter(
        (s) => s.label.includes(state.text) || s.key.includes(state.text),
      )
      return obj
    })
    .filter((v) => v.options.length > 0)
})
onMounted(() => {
  if (props.modelValue) {
    // ref_textarea.value.innerHTML = props.modelValue
    // ref_textarea.value.dispatchEvent(
    //   new Event('input', {
    //     bubbles: true,
    //     cancelable: true,
    //   }),
    // )
    ref_textarea.value.innerHTML = props.modelValue.replace(
      /\{\{#([^#]+)#\}\}/g,
      (match, p1) => {
        const k = [
          p1.substring(0, p1.indexOf('.')),
          p1.substring(p1.indexOf('.') + 1),
        ]
        const vars = state.optionsMap.get(`${k[0]}.${k[1]}`)
        const dom = document.createElement('div')
        dom.appendChild(initVarsDom(vars))
        return dom.innerHTML
      },
    )
  }
  if (!props.readonly) {
    document.addEventListener('selectionchange', handleSelectionchange)
    document.addEventListener('mousedown', onMouseDown)
  }
})
onUnmounted(() => {
  if (!props.readonly) {
    document.removeEventListener('selectionchange', handleSelectionchange)
    document.removeEventListener('mousedown', onMouseDown)
  }
})
</script>

<style scoped lang="scss">
@use '@/views/workflow/instance/component/style';

.filter {
  padding: 10px;
  border-bottom: style.$borderStyle;
}
</style>
