<script setup lang="ts">
import obj from '#/utils/obj'

import { mj_form_button_group } from '../mj-table'

defineOptions({ name: 'MjForm' })

const props = defineProps<{
  options: any
  showReset?: boolean
}>()

const emits = defineEmits(['submit', 'reset'])
// 编辑表单
const defaultFormOptions = {
  titleWidth: 80,
  titleAlign: 'right',
  // titleBold: true,
  data: props.options.data || {},
}

const formOptions = ref(defaultFormOptions as any)

const formEvents = {
  submit() {
    emits('submit')
  },
  reset() {
    emits('reset')
  },
} as any

const formButtonItemField = 'formButtonItemField'
const btnSubmit = {
  type: 'submit',
  content: '提交',
  status: 'primary',
  icon: 'vxe-icon-send',
}
const btnReset = {
  type: 'reset',
  content: '重置',
  icon: 'vxe-icon-repeat',
}
const formButtonItem = {
  field: formButtonItemField,
  align: 'right',
  span: 24,
  itemRender: {
    name: mj_form_button_group,
    options: [],
  },
} as any

let allItems = [] as any
let allSubItems = {} as any
// 字段索引，用于生成field
let fieldIndex = 0

// 初始化item
const initItem = (item: any, rules: any[]) => {
  // 如果没有field，随机生成一个
  // 如果缺少field，会影响showItems和hideItems的效果
  if (!item.field) {
    item.field = `mj_form_field_${fieldIndex}`
  }
  fieldIndex += 1

  if (item.children?.length) {
    // 子item
    for (const child of item.children) {
      initItem(child, rules)
    }
    return
  }

  if (!item.itemRender?.name) return
  // 必填
  let tip = '请选择'
  const renderName = item.itemRender.name.toLowerCase() as string
  if (renderName.includes('input') || renderName.includes('textarea')) {
    tip = '请输入'
  }
  const msg = `${tip}${item.title || ''}`
  // 占位
  if (!item.itemRender.props?.placeholder) {
    const props = item.itemRender.props || {}
    props.placeholder = msg
    item.itemRender.props = props
  }

  // 如果已经有规则，直接跳过
  if (rules[item.field]) return
  let itemRules = [] as any
  if (Array.isArray(item.rules)) {
    itemRules = [...itemRules, ...item.rules]
  }
  if (item.required || item.pattern || item.validator) {
    itemRules.push({
      required: item.required,
      pattern: item.pattern,
      validator: item.validator,
      message: item.message,
    })
  }
  for (const rule of itemRules) {
    if (!rule.message) {
      rule.message = msg
    }
  }
  rules[item.field] = itemRules
}

// 初始化编辑表单
const initForm = () => {
  // 按钮
  formButtonItem.itemRender.options = props.showReset
    ? [btnSubmit, btnReset]
    : [btnSubmit]
  // 编辑表单
  const formOptionsTmp = {
    ...defaultFormOptions,
  } as any
  obj.cover(formOptionsTmp, props.options)
  // formOptionsTmp.data = formOptions.value.data || {}
  formOptionsTmp.items = [...(props.options?.items || []), formButtonItem]
  formOptions.value = formOptionsTmp

  // rules
  const rules = { ...(props.options?.rules || ({} as any)) }

  allSubItems = {}
  allItems = formOptionsTmp.items || []
  fieldIndex = 0
  for (const item of allItems) {
    initItem(item, rules)
    if (item.children?.length) {
      allSubItems[item.field] = item.children
    }
  }
  formOptions.value.rules = rules
}

onMounted(() => {
  // initForm()
})

watch(
  [() => props.options.items, () => props.options.rules],
  () => {
    initForm()
  },
  {
    immediate: true,
    // 深度监听
  },
)

/**
 * 排除item
 * @param fieldSet 排除的field
 * @param curItems 当前的items
 * @param hiddenFields 最终被隐藏的field
 */
const _excludeItems = (
  fieldSet: Set<string>,
  curItems: any[],
  hiddenFields: any[],
) => {
  const newItems = [] as any
  for (const item of curItems) {
    if (fieldSet.has(item.field)) {
      // 发现了要隐藏的
      hiddenFields.push(item.field)
      continue
    }
    if (item.children?.length) {
      item.children = _excludeItems(fieldSet, item.children, hiddenFields)
      if (item.children?.length) {
        // 如果还有children，才添加
        newItems.push(item)
      }
      continue
    }
    newItems.push(item)
  }
  return newItems
}

const getItemsFields = (items: any) => {
  const fields = [] as any[]
  for (const item of items) {
    if (item.children?.length) {
      fields.push(...getItemsFields(item.children))
      continue
    }
    if (item.field) {
      fields.push(item.field)
    }
  }
  return fields
}

/**
 * 包含item
 * @param fieldSet 包含的field
 * @param curItems 当前的items
 */
const _includeItems = (fieldSet: Set<string>, curItems: any[]) => {
  const newItems = [] as any
  for (const item of curItems) {
    if (fieldSet.has(item.field)) {
      newItems.push(item)
      continue
    }
    if (item.children?.length) {
      item.children = _includeItems(fieldSet, allSubItems[item.field])
      if (item.children?.length) {
        // 如果还有children，才添加
        newItems.push(item)
      }
      continue
    }
  }
  return newItems
}

defineExpose({
  updateData(data: any) {
    if (!data) return
    obj.cover(formOptions.value.data, data)
  },
  setData(data: any) {
    formOptions.value.data = data
  },
  getData() {
    return formOptions.value.data
  },
  getOptions() {
    return formOptions.value
  },
  getCurrentItems() {
    return formOptions.value.items
  },
  getAllItems() {
    return allItems
  },
  hideItems(fields: any) {
    if (!Array.isArray(fields)) {
      fields = [fields]
    }
    if (!fields?.length) return
    // 当前最新的
    const curItems = formOptions.value.items
    const hiddenFields = [] as any
    const newItems = _excludeItems(new Set(fields), curItems, hiddenFields)
    if (!hiddenFields.length) return
    formOptions.value.items = newItems
  },
  showItems(fields: any) {
    if (!Array.isArray(fields)) {
      fields = [fields]
    }
    if (!fields?.length) return
    // 当前最新的
    const curItems = formOptions.value.items

    // 合并field
    const fieldSet = new Set([
      formButtonItemField,
      ...fields,
      ...getItemsFields(curItems),
    ])
    // 从全部items里面抽取
    const newItems = _includeItems(fieldSet, allItems)
    // if (curItems.length === newItems.length) return
    formOptions.value.items = newItems
  },
})
</script>

<template>
  <vxe-form v-bind="formOptions" v-on="formEvents">
    <template v-for="(_, slotName) in $slots" #[slotName]="slotProps">
      <slot :name="slotName" v-bind="slotProps || {}"></slot>
    </template>
  </vxe-form>
</template>

<style lang="scss" scoped></style>
