<script setup lang="ts">
import type { VxeFormProps, VxeModalProps } from 'vxe-pc-ui'

import type { MjEditFormProps } from '../const'

import { BasicService } from '#/api/service/basic'
import hud from '#/utils/hud'
import obj from '#/utils/obj'

import {
  mj_action_add,
  mj_action_update,
  mj_form_button_group,
  resetSelfItems,
} from '../const'

defineOptions({ name: 'MjEditForm' })

const props = defineProps<{
  options: MjEditFormProps
  service: BasicService
}>()

// 事件
const emit = defineEmits<{
  (e: 'editFormVisibleChanged', visible: boolean, edit: boolean): void
  // 数据修改了（添加、更新、删除成功）
  (
    e: 'dataModified',
    val: {
      action: number
      data: any
    },
  ): void
}>()

// 编辑表单弹框
const editModalVisible = ref(false)
const defaultEditModalOptions = {
  escClosable: true,
  showMaximize: true,
} as VxeModalProps
const editModalOptions = ref(defaultEditModalOptions)

// 不需要响应式
// 重置的表单数据
let resetFormData = {} as any
// 添加表单时的默认数据
let addFormData = {} as any

// 编辑表单所有的items
let allItems = [] as any

// 编辑表单
const defaultEditFormOptions = {
  titleWidth: 80,
  titleAlign: 'right',
  titleBold: true,
  data: {},
} as VxeFormProps

const editFormOptions = ref(defaultEditFormOptions)

// 编辑表单事件
const editFormEvents = {
  submit() {
    // 解除响应式
    let submitData = toRaw(editFormOptions.value.data)

    if (!submitData.id && props.options?.addFields?.length) {
      const keys = props.options?.addFields as any[]
      submitData = obj.filterKeys(submitData, keys)
    }
    // 请求前的处理
    const fn = props.options?.beforeSaveOrUpdate
    if (fn) {
      submitData = fn(submitData)
      if (!submitData) {
        return
      }
    }

    if (submitData.id) {
      // 如果是编辑，只更新部分字段
      let filterEditFormData = resetFormData
      const keys = props.options?.updateFields as any[]
      if (keys?.length) {
        submitData = obj.filterKeys(submitData, keys)
        filterEditFormData = obj.filterKeys(resetFormData, keys)
      }

      // 如果没有任何变化
      if (obj.isEqualIgnoreNumberString(submitData, filterEditFormData)) {
        hud.error('没有任何变化，无需提交')
        return
      }
    }

    // 保存数据
    hud.load({
      task: async () => {
        await props.service.saveOrUpdate(submitData)
      },
      success: async () => {
        editModalVisible.value = false
        emit('dataModified', {
          action: submitData.id ? mj_action_update : mj_action_add,
          data: submitData,
        })
      },
      option: '保存',
      hideSuccess: false,
    })
  },
  reset() {
    // 内部处理
    const oldData = toRaw(editFormOptions.value.data)
    let resetData = Object.assign({}, resetFormData)
    if (props.options?.beforeReset) {
      resetData = props.options?.beforeReset(resetData)
    }
    editFormOptions.value.data = resetData

    // 调用自定义表单控件的reset方法
    resetSelfItems(
      editFormOptions.value.items!,
      oldData,
      editFormOptions.value.data,
    )
  },
}

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

// 初始化编辑表单
function initEditForm() {
  // 弹框
  const editModalOptionsTmp = { ...defaultEditModalOptions }
  obj.cover(editModalOptionsTmp, props.options?.modal)
  editModalOptions.value = editModalOptionsTmp

  // 添加表单时的默认数据
  addFormData = {
    ...props.options?.defaultFormData,
  }

  // 编辑表单
  const editFormOptionsTmp = {
    ...defaultEditFormOptions,
  }
  obj.cover(editFormOptionsTmp, props.options?.form)
  editFormOptionsTmp.data = editFormOptions.value.data || {}
  editFormOptionsTmp.items = [
    ...(props.options?.form.items || []),
    formButtonItem,
  ]
  // 根据editFormOptionsTmp设置添加表单的默认值
  for (const item of editFormOptionsTmp.items) {
    if (!item.field) continue
    if (item.field in addFormData) continue
    const options = item.itemRender?.options
    if (!options?.length) continue
    addFormData[item.field] = options[0].value
  }
  editFormOptions.value = editFormOptionsTmp
  allItems = editFormOptionsTmp.items || []

  if (props.options?.addForm) {
    allItems = [...(props.options.addForm?.items || []), ...allItems]
  }
  if (props.options?.updateForm) {
    allItems = [...(props.options.updateForm?.items || []), ...allItems]
  }

  // rules
  const rules = props.options?.form.rules || ({} as any)
  for (const item of allItems) {
    if (!item.itemRender?.name) continue
    // 必填
    let tip = '请输入'
    const renderName = item.itemRender.name.toLowerCase() as string
    if (
      renderName.includes('select') ||
      renderName.includes('radio') ||
      renderName.includes('switch') ||
      renderName.includes('image') ||
      renderName.includes('upload')
    ) {
      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]) continue
    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
  }
  editFormOptions.value.rules = rules
}

// 新增还是编辑
let isEditFormEdit = false

// 新增
function add() {
  // 调整rules、items
  if (props.options?.addForm) {
    const { rules, items } = props.options.addForm
    if (rules) {
      editFormOptions.value.rules = {
        ...rules,
        ...props.options.form?.rules,
      }
    }
    if (items) {
      editFormOptions.value.items = [...items, formButtonItem]
    }
  }

  isEditFormEdit = false

  // 这里直接使用引用数据
  resetFormData = Object.assign({}, addFormData)

  editModalOptions.value.title = '新增'
  // 复制对象，避免修改原数据
  let data = Object.assign({}, resetFormData)
  if (props.options?.beforeEditFormShow) {
    data = props.options.beforeEditFormShow(data, false)
  }
  editFormOptions.value.data = data
  editModalVisible.value = true
}

// 编辑
function edit(row: any) {
  // 调整rules、items
  if (props.options?.updateForm) {
    const { rules, items } = props.options.updateForm
    if (rules) {
      editFormOptions.value.rules = {
        ...rules,
        ...props.options.form?.rules,
      }
    }
    if (items) {
      editFormOptions.value.items = [...items, formButtonItem]
    }
  }

  isEditFormEdit = true

  resetFormData = Object.assign({}, row)

  editModalOptions.value.title = '编辑'
  // 复制对象，避免修改原数据
  let data = Object.assign({}, resetFormData)
  if (props.options?.beforeEditFormShow) {
    data = props.options.beforeEditFormShow(data, true)
  }
  editFormOptions.value.data = data
  editModalVisible.value = true
}

// 监听表单对话框的显示和隐藏
watch(
  () => editModalVisible.value,
  (visible) => {
    emit('editFormVisibleChanged', visible, isEditFormEdit)

    if (!visible && props.options?.onEditFormHide) {
      props.options.onEditFormHide(isEditFormEdit)
    }
  },
)

watch(
  () => props,
  () => {
    initEditForm()
  },
  {
    immediate: true,
    // 深度监听
    deep: true,
  },
)

defineExpose({
  add,
  edit,
  update(data: any) {
    if (!data) return
    obj.cover(editFormOptions.value.data, data)
  },
  getData() {
    return editFormOptions.value.data
  },
  getOptions() {
    return editFormOptions.value
  },
  getItems() {
    return allItems
  },
})
</script>

<template>
  <!-- 如果给vxe-modal加class属性，会报错：
   [Vue warn]: Extraneous non-props attributes (class) were passed to component
   but could not be automatically inherited
   because component renders fragment or text or teleport root nodes. -->
  <vxe-modal v-model="editModalVisible" v-bind="editModalOptions">
    <template #default>
      <vxe-form v-bind="editFormOptions" v-on="editFormEvents">
        <template v-for="(_, slotName) in $slots" #[slotName]="slotProps">
          <slot :name="slotName" v-bind="slotProps || {}"></slot>
        </template>
      </vxe-form>
    </template>
  </vxe-modal>
</template>

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