<script setup lang="ts">
import XReportDesign from '@af-mobile-client-vue3/components/data/XReportGrid/XReportDesign.vue'
import { getConfigByName, runLogic } from '@af-mobile-client-vue3/services/api/common'
import { useUserStore } from '@af-mobile-client-vue3/stores/modules/user'
import { executeStrFunctionByContext } from '@af-mobile-client-vue3/utils/runEvalFunction'
import {
  showDialog,
  Button as VanButton,
  Card as VanCard,
  Radio as VanRadio,
  RadioGroup as VanRadioGroup,
  Row as VanRow,
  Skeleton as VanSkeleton,
  Space as VanSpace,
} from 'vant'
import { computed, nextTick, onBeforeMount, onBeforeUnmount, onMounted, provide, ref, watch } from 'vue'

// import HtmlToPdf from '@/utils/htmlToPDF'

// Props
const props = defineProps({
  files: {
    type: Array,
    default: () => [],
  },
  authority: {
    type: String,
    default: 'user',
  },
  editMode: {
    type: Boolean,
    default: true,
  },
  configName: {
    type: String,
    required: true,
  },
  activatedSlotName: {
    type: String,
    default: undefined,
  },
  localConfig: {
    type: Object,
    default: undefined,
  },
  dontFormat: {
    type: Boolean,
    default: true,
  },
  showImgInCell: {
    type: Boolean,
    default: false,
  },
  configData: {
    type: Object,
    default: undefined,
  },
  serverName: {
    type: String,
    default: '',
  },
  env: {
    type: String,
    default: 'prod',
  },
  displayOnly: {
    type: Boolean,
    default: true,
  },
  noPadding: {
    type: Boolean,
    default: true,
  },
  noTopBorder: {
    type: Boolean,
    default: false,
  },
  showTitle: {
    type: Boolean,
    default: true,
  },
  showSaveButton: {
    type: Boolean,
    default: false,
  },
  registerMap: {
    type: Array,
    default: undefined,
  },
  isWidget: {
    type: Boolean,
    default: true,
  },
  useOssForImg: {
    type: Boolean,
    default: true,
  },
  imgPrefix: {
    type: String,
    default: undefined,
  },
})

// Emits
const emit = defineEmits(['updateImg', 'selectRow', 'cancel', 'register'])

// Store
const userStore = useUserStore()
// const { user: currUser } = storeToRefs(userStore)

// Refs
// const XReportDesign = ref()
const xAddReport = ref()
const xReportDrawer = ref()

// 状态
const showSkeleton = ref(true)
const config = ref()
const type = ref('design')
const onlyDisplay = ref(false)
const _maxColSpan = ref(12)
const scanFinish = ref(false)
const activeConfig = ref(null)
const originalConfig = ref(null)
const configFromWeb = ref({})
const timer = ref()
const hasImages = ref(false)
const imageList = ref([])
const dataCache = ref()
const diff = ref([])
const globalData = ref({})

// Computed
const widget = computed(() => props.isWidget)

// 在provide之前定义getGlobalData函数
const getGlobalData = () => globalData.value

function setGlobalData(obj: any) {
  globalData.value = obj
}

// 然后再进行provide
provide('runLogic', runLogic)
provide('openDialog', openDialog)
provide('registerComponent', registerComponent)
provide('getComponentByName', getComponentByName)
provide('getParentComponentByName', getComponentByName)
provide('getConfigByName', getConfigByName)
provide('isWidget', widget)
// provide('currUser', currUser)
provide('setGlobalData', setGlobalData)
provide('getGlobalData', getGlobalData)

// Methods
function slotRendered() {
  if (config.value?.mountedFunction) {
    let func = config.value.mountedFunction
    if (func && func.startsWith('function')) {
      func = func.replace('function', 'async function')
      executeStrFunctionByContext(null, func, [])
    }
  }
}

function registerComponent(componentName: string, component: any) {
  if (XReportDesign.value)
    XReportDesign.value[componentName] = component
}

// 数据处理方法
function transformArray(inputList: any[]) {
  let operationIndex = 0
  let operationList = []
  const outputList = []
  for (const lst of inputList) {
    if (lst.length >= 1 && !lst.every(x => Array.isArray(x) || Array.isArray(lst[0]) || x.rowSpan === lst[0].rowSpan)) {
      operationList = lst
      break
    }
    else {
      operationIndex += 1
    }
  }

  let maxMergeRow = 0

  if (operationList.length === 0) {
    return inputList
  }
  else {
    const maxRow = Math.max(...operationList.map(item => item.rowSpan))
    let mergeIndexCol = 0
    for (let index = 0; index < operationList.length; index++) {
      const row = operationList[index]
      let rowSpan = row.rowSpan
      let mergeIndexRow = operationIndex + 1
      const rows = []
      if (rowSpan < maxRow && mergeIndexRow < inputList.length)
        rows.push([row])

      while (rowSpan < maxRow && mergeIndexRow < inputList.length) {
        rowSpan += inputList[mergeIndexRow][mergeIndexCol].rowSpan
        rows.push([inputList[mergeIndexRow][mergeIndexCol]])
        if (mergeIndexRow > maxMergeRow)
          maxMergeRow = mergeIndexRow

        mergeIndexRow++
      }
      if (rows.length !== 0)
        operationList[index] = rows

      if (row.rowSpan !== maxRow)
        mergeIndexCol++
    }
  }

  let putindex = 0
  while (operationIndex > 0) {
    outputList.push(inputList[putindex])
    putindex++
    operationIndex -= 1
  }

  outputList.push(operationList)

  while (maxMergeRow < inputList.length - 1) {
    outputList.push(inputList[maxMergeRow + 1])
    maxMergeRow += 1
  }

  return transformArray(outputList)
}

// 组件操作方法
function getComponentByName(componentName: string) {
  return XReportDesign.value?.[componentName]
}

// 对话框操作
function openDialog(configName: string, selectedId: any, mixinData: any, outEnv = {}, attr = {}) {
  xAddReport.value?.init({
    configName,
    selectedId,
    mixinData,
    outEnv,
    attr,
  })
}

function openDrawer(configName: string, selectedId: any, mixinData: any, outEnv = {}, attr = {}) {
  xReportDrawer.value?.init({
    configName,
    selectedId,
    mixinData,
    outEnv,
    attr,
  })
}

// 数据导出和打印
function updateImg(data: any) {
  emit('updateImg', data)
}

function exportData() {
  let tempData
  if (!activeConfig.value) {
    tempData = originalConfig.value.data
  }
  else {
    const tempDataKeys = Object.keys(activeConfig.value.tempData)
    tempDataKeys.forEach((key) => {
      changeDeepObject(activeConfig.value.data, key, activeConfig.value.tempData[key])
    })
    tempData = activeConfig.value.data
  }

  diff.value = []
  compareProps(tempData, dataCache.value)

  diff.value.forEach((eachDiff) => {
    const arr = eachDiff.split('.')
    let targetData = tempData[arr[0]]
    if (arr.length !== 1) {
      for (let i = 1; i < arr.length - 1; i++) {
        const path = arr[i]
        targetData = targetData[path]
      }
    }
    targetData.update = true
  })

  return tempData
}

function printDocument() {
  const printContent = (window as any).rawDocument.getElementById('printReady')
  // printElement(printContent)
  showDialog({ message: '操作成功！' })
}

function exportPDF() {
  const date = getDate()
  let title = config.value.title
  title = title.replace(/<[^>]+>/g, '')
  const fileName = `${date}${title}`
  // HtmlToPdf.getPdf(fileName, '#printReady')
}

// 配置处理方法
function configInit() {
  originalConfig.value = Object.assign({}, config.value)
  if (!props.dontFormat)
    formatConfigRow(config.value)

  activeConfig.value = config.value
  showSkeleton.value = false

  // 处理动态Index
  activeConfig.value.columns.forEach((row: any[]) => {
    row.forEach((cell) => {
      if (cell.dynamicDataIndex === true) {
        // eslint-disable-next-line no-new-func
        const func = new Function(`return ${cell.customFunctionForDynamicDataIndex}`)()
        cell.dataIndex = func(config.value)
      }
      if (['action', 'click'].includes(cell.eventType) && cell.customFunction && !cell.events) {
        cell.events = []
        cell.events.push({
          type: cell.eventType,
          customFunction: cell.customFunction,
        })
      }
    })
  })

  activeConfig.value.tempData = {}
  activeConfig.value.columns.forEach((row: any[]) => {
    row.forEach((cell) => {
      if (cell.dataIndex?.includes('@@@'))
        activeConfig.value.tempData[cell.dataIndex] = getDeepObject(activeConfig.value.data, cell.dataIndex)
    })
  })

  originalConfig.value.columns = transformArray(JSON.parse(JSON.stringify(config.value.columns)))

  scanFinish.value = true
}

// 格式化配置行方法
function formatConfigRow(config: any) {
  for (let i = 0; i < config.columns.length; i++) {
    // 对原始数组进行递归，依次将该位置拆分为三个部分，当前处理位置之前的，当前处理位置，当前处理位置之后的
    const before = config.columns.slice(0, i)
    const after = config.columns.slice(i + 1, config.columns.length)

    // 将当前处理的数组交给处理的方法
    const x = checkRow(config.columns[i])

    const newArr = []

    // 拼接之前的数组
    if (before.length > 0) {
      if (before.length >= 1) {
        before.forEach((item) => {
          newArr.push(item)
        })
      }
      else {
        newArr.push(before)
      }
    }

    // 拼接不需要更改当前节点处理完成的数组
    newArr.push(x.old)

    // 如果处理了新加的数据，拼接
    if (x.add.length > 0) {
      for (let j = 0; j < x.add.length; j++) {
        if (x.add[j]) {
          newArr.push(x.add[j])
          i++
        }
      }
    }

    // 拼接之后的数组
    if (after.length > 0) {
      if (after.length >= 1) {
        after.forEach((item) => {
          newArr.push(item)
        })
      }
      else {
        newArr.push(after)
      }
    }

    config.columns = newArr
  }
}

// 处理行数据
function checkRow(rowArr: any[]) {
  // 不需要更改的数据
  const original = []
  // 需要更改新加的数据
  const addArr = []
  // 统计rowspan出现的总和
  let count = 0
  // 统计声明列需要的rowspan总数
  let total = 0
  // 是否为声明行
  let titleCellFlag = false
  // 是否为声明行后第一行
  let firstSubLine = false
  // 需要处理的行，新的index值
  let subRowIndex = 0
  // 新生成的行，临时存储
  const waitForAddArr = []
  // 用于记录声明行的colspan避免格式错误
  let preColSpan = 0
  // 用于统计循环次数，判断是否是最后一次
  let forEachCount = 0

  // 标记所有数据
  rowArr.forEach((cell) => {
    forEachCount++
    // 如果该行没有rowspan则默认其为1，不要影响统计结果
    if (!cell.rowSpan)
      cell.rowSpan = 0

    if (cell.text && total !== 0) { // 如果遇到了下一个声明行，证明rowspan少了一行，需要补充一个占位格
      const nullObj = {
        type: 'placeHolderColumn',
        order: subRowIndex,
        noBoarder: true,
        needSplit: true,
        colSpan: preColSpan,
        dontShowRow: true,
      }
      subRowIndex++
      waitForAddArr.push(nullObj)
      total = 0
      count = 0
      titleCellFlag = false
      firstSubLine = false
    }
    else if ((total !== count + cell.rowSpan) && forEachCount === rowArr.length) {
      // 如果没有遇到了下一个声明行，但已经是当前行最后一个数据，也证明rowspan少了一行，需要补充一个占位格
      const nullObj = {
        type: 'placeHolderColumn',
        order: subRowIndex,
        noBoarder: true,
        needSplit: true,
        colSpan: preColSpan,
        dontShowRow: true,
      }
      subRowIndex++
      waitForAddArr.push(nullObj)
      total = 0
      count = 0
      titleCellFlag = false
      firstSubLine = false
    }

    // 判断是否为声明行
    if (cell.text && total === 0) {
      // 将声明行声明的rowspan作为总数，判断下方rowspan相加是否等于声明行声明的数量
      total = cell.rowSpan
      titleCellFlag = false
      firstSubLine = true
      subRowIndex = 1
      cell.show = true
      cell.showRowSpan = total
    }
    else if (cell.rowSpan > 0 && !titleCellFlag && firstSubLine) { // 判断是否为声明行后首行，因为首行不需要移动
      count += cell.rowSpan
      firstSubLine = false
      cell.noBoarder = true
      cell.show = true
      cell.showRowSpan = total
    }
    else if (cell.rowSpan > 0 && !titleCellFlag && !firstSubLine) { // 既非声明行，也非首行，需要移动
      count += cell.rowSpan
      cell.needSplit = true
      cell.order = subRowIndex
      cell.dontShowRow = true
      subRowIndex++

      // 如果之前添加过空行补充位置，刚好最后一位还有内容，将其互换
      if (forEachCount === rowArr.length && !waitForAddArr[waitForAddArr.length - 1].dataIndex) {
        waitForAddArr[waitForAddArr.length - 1].order += 1
        cell.order -= 1
        waitForAddArr.push(cell)
      }
      else {
        waitForAddArr.push(cell)
      }
    }

    // 如果count和total相等了，证明已经处理完成。将计数器还原
    if (count === total) {
      total = 0
      count = 0
      titleCellFlag = false
      firstSubLine = false
    }
    // 保存上一个的colspan，保持生成的格子与原格式一致
    preColSpan = cell.colSpan
  })

  // 将所有不需要移动的放入original
  rowArr.forEach((cell) => {
    if (cell.needSplit !== true)
      original.push(cell)
  })

  // 增加新的数组
  waitForAddArr.forEach((cell) => {
    const target = cell.order
    cell.noBoarder = true
    if (addArr[target] === undefined) {
      const temp = []
      temp.push(cell)
      addArr[target] = temp
    }
    else if (addArr[target].length > 0) {
      addArr[target].push(cell)
    }
  })

  // 如果没有新增，将单元格边框设置为显示
  if (addArr.length < 1) {
    original.forEach((cell) => {
      if (cell.type === 'input' || cell.type === 'inputs')
        cell.noBoarder = false
    })
  }

  return {
    old: original,
    add: addArr,
  }
}

// 配置扫描方法
function checkSlotDefine(config: any) {
  const slotsDeclare = config.slotsDeclare
  const total = slotsDeclare.length
  let count = 0
  slotsDeclare.forEach((declare: string) => {
    config.columns.forEach((row: any[]) => {
      row.forEach((cell) => {
        if (cell.slotConfig === declare) {
          count++
        }
      })
    })
  })
  return count === total
}

function scanConfigName(config: any, result: string[]) {
  if (config.slotsDeclare) {
    config.slotsDeclare.forEach((name: string) => {
      result.push(name)
    })
  }
}

function scanConfigSlot(config: any) {
  const columnsArr = config.columns
  for (let i = 0; i < columnsArr.length; i++) {
    for (let j = 0; j < columnsArr[i].length; j++) {
      if (columnsArr[i][j].type === 'slot') {
        const targetName = columnsArr[i][j].slotConfig
        if (!configFromWeb.value[targetName] || !configFromWeb.value[targetName].columns) {
          console.error('无法找到目标插槽的配置！')
          return
        }

        config.columns[i] = []
        const before = config.columns.slice(0, i)
        let after = config.columns.slice(i + 1, config.columns.length)

        const addArr = []
        for (let k = 0; k < configFromWeb.value[targetName].columns.length; k++) {
          const temp = []
          configFromWeb.value[targetName].columns[k].forEach((cell: any) => {
            temp.push(cell)
          })
          addArr.push(temp)
        }

        const newArr = []
        if (before.length > 0) {
          before.forEach((item) => {
            newArr.push(item)
          })
        }

        addArr.forEach((arr) => {
          newArr.push(arr)
        })

        if (after.length === 1) {
          if (after[0].type === 'slot' || after[0][0].type === 'slot')
            after = []
        }
        if (after.length > 0) {
          after.forEach((item) => {
            newArr.push(item)
          })
        }

        config.columns = newArr
        config.slotsDeclare = configFromWeb.value[targetName].slotsDeclare || []

        if (config.data.images && configFromWeb.value[targetName].data.images) {
          config.data.images = { ...config.data.images, ...configFromWeb.value[targetName].data.images }
          delete configFromWeb.value[targetName].data.images
        }
        config.data = { ...config.data, ...configFromWeb.value[targetName].data }
      }
    }
  }
  config.value = config
}

// 数据比较方法
function compareProps(obj1: any, obj2: any, path = '') {
  for (const key in obj1) {
    if (typeof obj2[key] === 'undefined') {
      diff.value.push(path + key)
    }
    else if (Array.isArray(obj1) && Array.isArray(obj2)) {
      if (obj1[key].length !== obj2[key].length)
        diff.value.push(path + key)
    }
    else if (typeof obj1[key] === 'object' && typeof obj2[key] === 'object') {
      compareProps(obj1[key], obj2[key], `${path + key}.`)
    }
    else if (obj1[key] !== obj2[key]) {
      diff.value.push(path + key)
    }
  }
}

// 数据处理辅助方法
function getDeepObject(obj: any, strPath: string) {
  const arr = strPath.split('@@@')
  let result = obj[arr[0]]
  arr.shift()
  try {
    while (arr.length > 0) {
      result = result[arr[0]]
      arr.shift()
    }
  }
  catch (e) {
    result = undefined
  }
  return result
}

function changeDeepObject(obj: any, strPath: string, newVal: any) {
  const arr = strPath.split('@@@')
  if (obj[arr[0]] === undefined)
    obj = obj.images

  if (arr.length === 1) {
    obj[arr[0]] = newVal
  }
  else {
    let result = obj[arr[0]]
    arr.shift()
    while (arr.length > 1) {
      result = result[arr[0]]
      arr.shift()
    }
    if (result)
      result[arr[0]] = newVal
  }
}

// 日期处理方法
function getDate() {
  const currentDate = new Date()
  const year = currentDate.getFullYear()
  const month = String(currentDate.getMonth() + 1).padStart(2, '0')
  const day = String(currentDate.getDate()).padStart(2, '0')
  return `${year}_${month}_${day}`
}

// 表格操作方法
function tabChanged(key: string) {
  scanFinish.value = false
  originalConfig.value.data = { ...originalConfig.value.data, ...config.value.data }
  config.value.data = originalConfig.value.data

  if (type.value === 'display') {
    const tempDataKeys = Object.keys(activeConfig.value.tempData)
    tempDataKeys.forEach((key) => {
      changeDeepObject(activeConfig.value.data, key, activeConfig.value.tempData[key])
    })

    let count = 0
    imageList.value = []
    const keys = Object.keys(config.value.data.images)
    keys.forEach((key) => {
      if (config.value.data.images[key].length > 0) {
        imageList.value = [...imageList.value, ...config.value.data.images[key]]
        count++
      }
    })
    hasImages.value = count > 0
  }
  else {
    hasImages.value = false
  }

  scanFinish.value = true
}

function saveConfig() {
  const funcStr = config.value.confirmFunction
  executeStrFunctionByContext(null, funcStr, [])
}

function cancelConfig() {
  emit('cancel')
}

// 在其他方法后面添加
function jsonConfigInit() {
  if (props.configData === undefined) {
    console.error('未找到数据！')
    return
  }

  originalConfig.value = Object.assign({}, config.value)
  originalConfig.value.data = JSON.parse(JSON.stringify(props.configData))
  type.value = 'display'
  // onlyDisplay.value = true
  showSkeleton.value = false

  nextTick(() => {
    scanFinish.value = true
  })
}

// 生命周期
onBeforeMount(() => {
  if (props.displayOnly) {
    onlyDisplay.value = true
    type.value = 'display'
  }

  if (props.localConfig) {
    if (props.localConfig.designMode === 'json') {
      config.value = props.localConfig
      if (props.configData !== undefined)
        config.value.data = props.configData

      jsonConfigInit()
    }
    else {
      config.value = props.localConfig
      if (props.configData !== undefined)
        config.value.data = props.configData

      if (config.value.data.images === undefined)
        config.value.data.images = {}

      configInit()
    }
  }
  else {
    getConfigByName(props.configName, (res: any) => {
      config.value = JSON.parse(JSON.stringify(res))
      if (config.value.designMode === 'json') {
        if (props.configData !== undefined)
          config.value.data = props.configData

        jsonConfigInit()
      }
      else {
        if (props.configData !== undefined)
          config.value.data = props.configData

        if (config.value.data.images === undefined)
          config.value.data.images = {}

        configInit()
      }
    }, props.serverName)
  }
})

onMounted(() => {
  if (props.registerMap !== undefined) {
    // eslint-disable-next-line vue/no-mutating-props
    props.registerMap.push({
      exportData,
      printDocument,
      exportPDF,
    })
  }

  if (props.configData)
    dataCache.value = JSON.parse(JSON.stringify(props.configData))
  else if (config.value?.data)
    dataCache.value = JSON.parse(JSON.stringify(config.value.data))
})

onBeforeUnmount(() => {
  clearInterval(timer.value)
})

// Watch
watch(() => props.configName, (val) => {
  if (val) {
    getConfigByName(props.configName, (res: any) => {
      config.value = res
      configInit()
    }, props.serverName)
  }
})

watch(() => props.localConfig, (val) => {
  if (val) {
    config.value = val
    configInit()
  }
})

// 暴露方法
defineExpose({
  exportData,
  printDocument,
  exportPDF,
  getComponentByName,
})

// 选择行方法
function selectRow(selectedRowKeys: any[], selectedRows: any[]) {
  emit('selectRow', selectedRowKeys, selectedRows)
}
</script>

<template>
  <div>
    <!--  骨架屏  -->
    <VanCard v-if="showSkeleton">
      <VanSkeleton active />
    </VanCard>
    <template v-if="noPadding">
      <!--   主体表格   -->
      <XReportDesign
        v-if="scanFinish"
        ref="XReportDesign"
        :show-img-in-cell="showImgInCell"
        :img-prefix="imgPrefix"
        :use-oss-for-img="useOssForImg"
        :display-only="displayOnly"
        :config="type === 'display' ? originalConfig : activeConfig"
        :slot-config-name="type === 'display' ? undefined : activatedSlotName"
        :for-display="type === 'display'"
        :server-name="serverName"
        :env="env"
        :show-title="showTitle"
        :no-padding="noPadding"
        :no-top-border="noTopBorder"
        :show-images="hasImages"
        :image-list="imageList"
        @update-img="updateImg"
        @select-row="selectRow"
        @slot-rendered="slotRendered"
      />

      <VanRow v-if="showSaveButton" type="flex" justify="end">
        <VanSpace>
          <VanButton @click="saveConfig">
            提交
          </VanButton>
          <VanButton @click="cancelConfig">
            取消
          </VanButton>
        </VanSpace>
      </VanRow>
    </template>
    <template v-else>
      <!--  用以包裹整个页面  -->
      <div v-if="!showSkeleton">
        <!--   切换菜单   -->
        <VanRadioGroup
          v-show="!onlyDisplay && editMode"
          v-model="type"
          default-value="a"
          button-style="solid"
          @change="tabChanged"
        >
          <VanRadio v-if="!onlyDisplay" name="design">
            设计
          </VanRadio>
          <VanRadio name="display" style="border-radius: 0">
            预览
          </VanRadio>
        </VanRadioGroup>

        <!--   主体表格   -->
        <XReportDesign
          v-if="scanFinish"
          ref="XReportDesign"
          :show-img-in-cell="showImgInCell"
          :img-prefix="imgPrefix"
          :use-oss-for-img="useOssForImg"
          :display-only="displayOnly"
          :config="type === 'display' ? originalConfig : activeConfig"
          :slot-config-name="type === 'display' ? undefined : activatedSlotName"
          :for-display="type === 'display'"
          :no-padding="noPadding"
          :no-top-border="noTopBorder"
          :show-title="showTitle"
          :server-name="serverName"
          :env="env"
          :show-images="hasImages"
          :image-list="imageList"
          @update-img="updateImg"
          @select-row="selectRow"
          @slot-rendered="slotRendered"
        />
      </div>
    </template>

    <!-- 弹出框 -->
    <!-- <x-add-report
      ref="xAddReport"
      :env="env"
    /> -->
    <!-- 弹出框 -->
    <!-- <x-report-drawer
      ref="xReportDrawer"
      :env="env"
    />  -->
  </div>
</template>

<style lang="less" scoped>
.tools {
  text-align: center;
  cursor: pointer;

  .toolsItem {
    display: inline-block;
  }
}
</style>
