<template>
  <div class="low-module" :class="[`low-module__${moduleInfo.id}`]" v-loading="loading">
    <ContentWrap class="mb-0!" :class="wrapClass">
      <el-tabs
        v-if="isInit"
        ref="tabsRef"
        v-model="tabsActive"
        :type="tabsOption.type"
        :tab-position="tabsOption.tabPosition"
        :before-leave="beforeLeave"
        @tab-change="tabChange"
        @tab-remove="tabRemove"
      >
        <template
          v-for="item in tabsOption.columnList.map((prop) => tabsOption.column[prop])"
          :key="item.prop"
        >
          <el-tab-pane
            v-if="item.display || item.display !== false"
            :label="item.label"
            :name="item.prop"
            :disabled="item.disabled"
            :closable="item.closable"
          >
            <template #label>
              <span class="custom-tabs-label flex items-center gap-x-2px">
                <Icon :size="14" v-if="item.icon" :icon="item.icon"></Icon>
                <span class="line-height-20px">{{ item.label }}</span>
              </span>
            </template>
            <div
              v-if="!item.isLazy || (item.isLazy && lazyObj[item.prop])"
              :class="['w-100%', item.prop, `tab-${item.prop}-${item.type}`, 'low-module-item']"
              :style="{ maxHeight: `calc(100vh - ${tableCalcNum - 40}px)` }"
            >
              <template v-if="item.type === 'table'">
                <TableView
                  :ref="(el) => (moduleRef[item.prop] = el)"
                  v-bind="item.params"
                  show-type="view"
                  :calc-num="tableCalcNum"
                  :class="{ 'pt-0!': isDefalutx }"
                ></TableView>
              </template>
              <template v-else-if="item.type === 'form'">
                <FormView
                  :ref="(el) => (moduleRef[item.prop] = el)"
                  v-bind="item.params"
                  show-type="view"
                  class="px-10px"
                  :class="{ 'low-form-pt-5': isDefalutx }"
                ></FormView>
              </template>
              <template v-else-if="item.type === 'report'">
                <div class="p-20px" :class="{ 'pt-0!': isDefalutx }">
                  <LowReport
                    :ref="(el) => (moduleRef[item.prop] = el)"
                    v-bind="item.params"
                    :calc-height="tableCalcNum - 20"
                  ></LowReport>
                </div>
              </template>
              <template v-else-if="item.type === 'custom'">
                <div class="module-item-custom p-20px" :class="{ 'pt-0!': isDefalutx }">
                  <component
                    :ref="(el) => (moduleRef[item.prop] = el)"
                    :is="getCustomComponent(item.params.fileUrl)"
                    v-bind="item.params || {}"
                  ></component>
                </div>
              </template>
            </div>
          </el-tab-pane>
        </template>
      </el-tabs>
      <div v-else class="h-auto" v-loading="!isInit"></div>
    </ContentWrap>
  </div>
  <!-- 增强注册的控件 -->
  <template v-for="item in rendControlData" :key="item.key">
    <component
      :ref="(el) => (componentRef[item.key] = el)"
      :is="componentObj[item.random]"
      v-bind="item.params || {}"
      v-model="item.show"
    ></component>
  </template>
</template>

<script setup lang="ts">
import TableView from '../TableView.vue'
import FormView from '../FormView.vue'
import LowReport from '../LowReport/index.vue'

import { getModelViewData } from '@/api/design/module/index'

import useCopyText from '@/hooks/design/useCopyText'
import controlPath from '../controlPath'
import { formattingStrFunction, assembleLengObj } from '@/utils/lowDesign'
import { setDeepObject, addScssStyle, delScssStyle } from '../utils/util'
import defaultUseObj from '../utils/defaultUseUtil'
import { registerComp } from '../utils/registerComponent'
import * as Vue from 'vue'
import { cloneDeep } from 'lodash-es'

interface Props {
  moduleId: string
  calcHeight?: number
  enhanceData?: object //传递给组合模块 js增强内部调用配置
}
interface JsEnhanceObj {
  initImport?: () => Promise<object>
  initOption?: () => void
  beforeSwitch?: (activeName: string, oldActiveName: string) => Promise<any>
  afterSwitch?: (tabName: string) => void
  beforeClose?: (tabName: string) => Promise<boolean>
}
const props = defineProps<Props>()

defineOptions({ name: 'LowModule' })

const message = useMessage() // 消息弹窗
const { mergeLocaleMessage, t } = useI18n() // 国际化
const route = useRoute()
const routes = useRouter()
const { copyText } = useCopyText()

const loading = ref(false)
const isInit = ref(false)

const moduleInfo = ref<any>({})
const tabsActive = ref('')
const tabsOption = ref<any>({})
const jsEnhanceObj = ref<JsEnhanceObj>({})
const lazyObj = ref<any>({})
const timerObj = ref<any>({})
let useImport = reactive({})

const componentObj = markRaw({ pathOnly: {} })
const rendControlData = ref<any>({})

const tabsRef = ref()
const moduleRef = ref({})
const componentRef = ref({})

const tableCalcNum = computed(() => {
  const { type, tabPosition } = tabsOption.value
  let defaultNum = props.calcHeight || 220
  if (['left', 'right'].includes(tabPosition)) defaultNum -= 40
  if (['border-card', 'card'].includes(type)) defaultNum -= 20
  else {
    if (tabPosition == 'top') defaultNum += 5
    if (tabPosition == 'bottom') defaultNum += 20
  }

  return defaultNum
})

const wrapClass = computed(() => {
  const { type, tabPosition } = tabsOption.value
  const className: string[] = []
  if (['border-card', 'card'].includes(type)) {
    className.push('no-padding')
  } else {
    if (tabPosition == 'top') className.push('body-pt-5')
    if (['left', 'right'].includes(tabPosition)) className.push('body-p-10-0-0')
  }

  return className
})

const isDefalutx = computed(() => {
  const { type, tabPosition } = tabsOption.value
  if (!['border-card', 'card'].includes(type) && ['left', 'right'].includes(tabPosition)) {
    return true
  }
  return false
})

watch(
  () => tabsActive.value,
  (val) => {
    if (val) {
      const currColumn = tabsOption.value.column[val]
      if (!currColumn) return
      const currRef = moduleRef.value[val]
      const isLazy = currColumn.isLazy
      const isRefresh = currColumn.isRefresh
      const type = currColumn.type
      try {
        if (isLazy && !lazyObj.value[val]) lazyObj.value[val] = true
        else {
          if (type == 'table') {
            currRef?.controlRef?.initTableLayout()
            if (isRefresh) currRef?.controlRef?.resetChange()
          } else if (type == 'form') {
            if (isRefresh) currRef?.initControl()
          } else if (type == 'report') {
            currRef?.initTableLayout()
            if (isRefresh) currRef?.resetChange()
          } else if (type == 'custom') {
            if (isRefresh && currRef?.refreshData) currRef?.refreshData()
          }
        }
      } catch (error) {}
    }
  },
  { immediate: true }
)

const initTabData = {
  general: [
    { label: 'label', value: 'tabName' },
    { label: 'icon', value: 'tabIcon' },
    { label: 'type', value: 'moduleType' },
    { label: 'isRefresh', value: 'isRefresh' },
    { label: 'isLazy', value: 'isLazy' }
  ],
  table: (value) => {
    return { tableId: value, fixedSearch: {}, enhanceData: {} }
  },
  form: (value) => {
    return {
      formId: value,
      showButton: false,
      formType: 'view',
      handleType: 'default',
      dataOption: { tableId: '', dataId: '' },
      defaultData: {},
      enhanceData: {},
      beforeClose: null
    }
  },
  report: (value) => {
    return { reportCode: value, fixedSearch: {} }
  },
  custom: (value) => {
    getComponentRandom(value)
    return { fileUrl: value }
  }
}

const initTabs = async () => {
  isInit.value = false
  const { tab, optionList, jsList } = await getModelViewData(props.moduleId)
  //国际化配置
  const fieldLengObj = assembleLengObj(optionList, 'tabI18n', 'tabCode', 'tabName')
  for (const key in fieldLengObj) {
    mergeLocaleMessage(key, { [props.moduleId]: fieldLengObj[key] })
  }
  //初始化标签页配置
  const column = {}
  const columnList: string[] = []
  optionList.forEach((item) => {
    columnList.push(item.tabCode)
    const columnItem = {
      disabled: false,
      closable: false,
      display: true,
      prop: item.tabCode,
      params: initTabData[item.moduleType] ? initTabData[item.moduleType](item.moduleValue) : {}
    }
    initTabData.general.forEach(({ label, value }) => {
      if (label == 'label') columnItem[label] = t(`${props.moduleId}.${item.tabCode}`)
      else if (label == value) columnItem[label] = item[value] === 'Y'
      else columnItem[label] = item[value]
    })
    column[item.tabCode] = columnItem
  })
  tabsOption.value = {
    type: tab.tabsType,
    tabPosition: tab.tabsPosition,
    stretch: false,
    column,
    columnList
  }
  moduleInfo.value = tab
  //初始化增强配置
  let jsEnhanceStr = ''
  let scssEnhanceStr = ''
  jsList.forEach((item) => {
    if (!jsEnhanceStr && item.jsType == 'js') jsEnhanceStr = item.jsJson
    if (!scssEnhanceStr && item.jsType == 'scss') scssEnhanceStr = item.jsJson
  })
  const errorText = '表单开发：js增强初始化异常，请检查增强内容' + props.moduleId
  jsEnhanceObj.value = executeJsEnhance(jsEnhanceStr, errorText) || {}

  try {
    if (jsEnhanceObj.value.initImport) useImport = await jsEnhanceObj.value.initImport()
  } catch (error) {
    enhanceErrorTip('js增强【initImport】方法执行异常，请检查', error)
  }
  try {
    if (jsEnhanceObj.value.initOption) jsEnhanceObj.value.initOption()
  } catch (error) {
    enhanceErrorTip('js增强【initOption】方法执行异常，请检查', error)
  }

  if (scssEnhanceStr) addScssStyle(scssEnhanceStr, props.moduleId, 'module')

  if (!tabsActive.value) tabsActive.value = columnList[0]
  isInit.value = true
}

const enhanceErrorTip = (tip, error) => {
  console.warn(`${tip}
    ${error}`)
}

const beforeLeave = (activeName, oldActiveName) => {
  try {
    if (jsEnhanceObj.value.beforeSwitch)
      return jsEnhanceObj.value.beforeSwitch(activeName, oldActiveName)
  } catch (error) {
    enhanceErrorTip('js增强【beforeSwitch】方法执行异常，请检查', error)
  }
  return true
}
const tabChange = (name) => {
  try {
    if (jsEnhanceObj.value.afterSwitch) jsEnhanceObj.value.afterSwitch(name)
  } catch (error) {
    enhanceErrorTip('js增强【afterSwitch】方法执行异常，请检查', error)
  }
}
const tabRemove = async (name) => {
  try {
    if (jsEnhanceObj.value.beforeClose) {
      const bool = await jsEnhanceObj.value.beforeClose(name).catch(() => false)
      if (bool) delete tabsOption.value.column[name]
    }
  } catch (error) {
    enhanceErrorTip('js增强【beforeClose】方法执行异常，请检查', error)
  }
}

const getComponentRandom = (path) => {
  if (!path) return ''
  let random = componentObj['pathOnly'][path]
  if (!random) {
    random = `key_${Math.ceil(Math.random() * 9999999)}`
    componentObj['pathOnly'][path] = random
    componentObj[random] = registerComp(path)
  }
  return random
}
const getCustomComponent = (fileUrl) => {
  const random = getComponentRandom(fileUrl)
  return componentObj[random] || ''
}

const executeJsEnhance = (enhance: string, errTip?: string) => {
  if (!enhance) return
  enhance = formattingStrFunction(enhance)
  try {
    const fun = eval(`(function getEnhance(){
      ${enhance}
    })`)
    return fun()
  } catch (error) {
    enhanceErrorTip(errTip, error)
  }
}

const initEnhanceUseFun = () => {
  return {
    controlInit: (type, refKey, params) => {
      const path = controlPath[type]?.path || ''
      if (path) {
        const random = getComponentRandom(path)
        rendControlData.value[refKey] = { show: false, key: refKey, random, params }
      }
    },
    /**
     * 切换标签
     * @param tabName 标签编码
     */
    switchTab: (tabName) => {
      if (!tabsOption.value.columnList.includes(tabName)) {
        enhanceErrorTip(`调用useFun.switchTab方法，标签编码【${tabName}】不存在`, '')
        return false
      }
      tabsActive.value = tabName
    },
    /**
     * 获取标签配置
     * @param prop 标签编码
     * @param isDeep 是否深拷贝
     * @param configKey 配置key
     */
    getPropConfig: (prop: string, configKey?: string, isDeep?: boolean) => {
      let config: any = tabsOption.value.column[prop]
      if (configKey) return config ? config[configKey] : config
      return isDeep ? cloneDeep(config) : config
    },
    /**
     * 设置标签配置
     * @param prop 标签编码
     * @param config 需修改的配置，非覆盖，格式Object,支持深结构修改 例：'params.deep.deep'
     */
    setPropConfig: (prop, config) => {
      const curConfig = useFun.getPropConfig(prop)
      if (!curConfig) enhanceErrorTip(`调用useFun.setPropConfig方法，未找到标签：${prop}`, '')
      else {
        if (config?.['params.fileUrl']) getComponentRandom(config['params.fileUrl'])
        else if (config?.params?.fileUrl) getComponentRandom(config.params.fileUrl)
        setDeepObject(curConfig, config)
      }
    },
    /**
     * 添加标签
     * @param prop 标签编码
     * @param config 标签配置
     * @param index 添加位置
     */
    addTabData: (prop, config, index?) => {
      if (tabsOption.value.columnList.includes(prop)) {
        enhanceErrorTip(`调用useFun.addTabData方法，【${prop}】标签编码已存在`, '')
        return false
      }
      if (config.type == 'custom') getComponentRandom(config.params.fileUrl)
      tabsOption.value.column[prop] = { ...config, prop }
      if (index !== undefined && !isNaN(Number(index))) {
        index = index - 1 >= 0 ? index - 1 : 0
        tabsOption.value.columnList.splice(index, 0, prop)
      } else tabsOption.value.columnList.push(prop)
    },
    /**
     * 删除标签
     * @param prop 标签编码
     */
    deleTabData: (prop) => {
      if (!tabsOption.value.columnList.includes(prop)) {
        enhanceErrorTip(`调用useFun.deleTabData方法，标签编码【${prop}】不存在`, '')
        return false
      }
      delete tabsOption.value.column[prop]
      tabsOption.value.columnList = tabsOption.value.columnList.filter((key) => key != prop)
      if (tabsActive.value == prop) {
        tabsActive.value = tabsOption.value.columnList[0]
      }
    },
    getVue: () => Vue,
    copyText, //复制到剪切板
    ...defaultUseObj
  }
}

const useFun = reactive(initEnhanceUseFun())

onMounted(() => {
  initTabs()
})

onUnmounted(() => {
  delScssStyle(props.moduleId, 'module')
  for (let key in timerObj.value) {
    clearInterval(timerObj.value[key])
    clearTimeout(timerObj.value[key])
  }
})

defineExpose({
  moduleInfo,
  tabsRef,
  moduleRef,
  tabsOption,
  tabsActive,
  loading,
  useFun
})
</script>

<style lang="scss" scoped>
.low-module {
  & > :deep(.el-card) {
    .el-card__body > div > .el-tabs {
      &.el-tabs--border-card {
        .el-tabs__content {
          padding: 0;
        }
      }

      &.el-tabs--top {
        & > .el-tabs__header {
          margin-bottom: 0 !important;
        }

        &.el-tabs--card > .el-tabs__header .el-tabs__nav {
          border-top: 0;
          border-left: 0;
        }
      }

      &.el-tabs--bottom {
        & > .el-tabs__header {
          margin-top: 0 !important;
        }

        &.el-tabs--card > .el-tabs__header {
          border-bottom: 0;

          .el-tabs__nav {
            border-bottom: 0;
            border-left: 0;

            .el-tabs__item {
              border-bottom: 0;
            }
          }
        }
      }

      &.el-tabs--left {
        & > .el-tabs__header {
          margin-right: 0 !important;
        }

        &.el-tabs--card > .el-tabs__header .el-tabs__nav {
          border-top: 0;
          border-left: 0;
        }
      }

      &.el-tabs--right {
        & > .el-tabs__header {
          margin-left: 0 !important;
        }

        &.el-tabs--card > .el-tabs__header .el-tabs__nav {
          border-top: 0;
          border-right: 0;
        }
      }

      &.el-tabs--left,
      &.el-tabs--right {
        .el-tabs__header {
          height: auto;
        }

        &.el-tabs--card,
        &.el-tabs--border-card {
          .el-tabs__header {
            border-bottom: 0;
          }
        }
      }
    }

    &.no-padding {
      .el-card__body {
        padding: 0;

        & > div > .el-tabs--border-card {
          border: 0;
        }
      }
    }

    &.body-pt-5 .el-card__body {
      padding-top: 5px;
    }

    &.body-p-10-0-0 .el-card__body {
      padding: 10px 0 0;
    }
  }

  .low-module-item {
    overflow-y: auto;

    & > div > :deep(.low-table) > .low-table-content > div > .avue-crud > .avue-crud__pagination {
      padding-bottom: 0;
    }

    & > div > :deep(.low-reoprt) > .avue-crud > .avue-crud__pagination {
      padding-bottom: 0;
    }
  }

  :deep(.low-form-pt-5) > .low-form {
    padding-top: 5px;
  }
}
</style>
