<!-- eslint-disable unicorn/no-negated-condition -->
<!-- eslint-disable unicorn/explicit-length-check -->
<script setup lang="ts">
import type { VxeFormProps } from 'vxe-pc-ui'
import type { VxeGridProps } from 'vxe-table'

import type { MjTableProps, SelectedItems } from './const'

import { ref, toRaw, watch } from 'vue'

import { CloseBold, Delete, Edit, Plus } from '@element-plus/icons-vue'
import { VxeGrid } from 'vxe-table'

import hud from '#/utils/hud'
import obj from '#/utils/obj'

import {
  mj_action_remove,
  mj_empty_data,
  mj_form_input,
  resetSelfItems,
} from './const'
import MjEditForm from './mj-edit-form/index.vue'

defineOptions({
  name: 'MjTable',
})

// 属性
const props = withDefaults(defineProps<MjTableProps>(), {
  gridEvents: {} as any,
  multiSelectable: true,
  addButtonText: '新增',
  deleteButtonText: '删除',
})

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

// 当前的数据
let _currentRows = [] as any

const _innerSelectedItems = ref({} as SelectedItems)
const _selectedItems = computed({
  get: () => {
    return props.selectedItems === undefined
      ? _innerSelectedItems.value
      : props.selectedItems
  },
  set: (val) => {
    if (props.selectedItems === undefined) {
      _innerSelectedItems.value = val
    } else {
      emit('update:selected-items', val as any)
    }
  },
})

// 编辑表单
const editFormRef = ref()

// 权限控制
const _hideEdit = ref(false)
const _hideRemove = ref(false)
const _hideAdd = ref(false)
const _hideCheckbox = ref(false)
const _hideSearch = ref(false)

// 权限控制
function initPermissions() {
  if (!props.editForm) {
    // 没有表单
    _hideEdit.value = true
    _hideAdd.value = true
  } else {
    _hideEdit.value = props.hideEdit || false
    _hideAdd.value = props.hideAdd || false
  }
  _hideRemove.value = props.hideRemove || false
  _hideCheckbox.value =
    _hideRemove.value && !props.selectable && props.selectedItems === undefined
  _hideSearch.value = props.hideSearch || false
}

// 表格
const gridRef = ref()

// 搜索表单
const defaultSearchFormOptions = {
  titleBold: true,
  data: {},
  collapseNode: true,
} as VxeFormProps

// 表格的配置
const checkedProp = 'checked'
const defaultGridOptions = {
  height: 'auto',
  showOverflow: true,
  border: false,
  rowConfig: {
    isCurrent: true,
    isHover: true,
  },
  pagerConfig: {
    total: 0,
    currentPage: 1,
    pageSizes: [25, 50, 100],
    pageSize: 25,
    layouts: [
      'Home',
      'PrevJump',
      'PrevPage',
      'Number',
      'NextPage',
      'NextJump',
      'End',
      'Sizes',
      'FullJump',
      'Total',
    ],
  },
  proxyConfig: {
    ajax: {
      query: loadPageData,
    },
  },
  toolbarConfig: {
    slots: {
      buttons: 'toolbarButtons',
    },
  },
  checkboxConfig: {
    highlight: true,
    checkField: checkedProp,
  },
  data: [],
} as VxeGridProps
const gridOptions = ref(defaultGridOptions)

// 初始化表格
function initGrid() {
  // 表格的列
  const gridColumns = []
  if (!_hideCheckbox.value) {
    gridColumns.push({
      type: 'checkbox',
      fixed: 'left',
      width: 50,
      slots: { header: 'checkbox_header', checkbox: 'checkbox_cell' },
    })
  }
  if (!_hideEdit.value) {
    gridColumns.push({
      title: '编辑',
      fixed: 'left',
      width: 80,
      slots: { default: 'mj_edit_default' },
    })
  }

  // 表格合并
  const gridOptionsTmp = { ...defaultGridOptions, data: gridOptions.value.data }
  gridOptionsTmp.columns = gridColumns as any
  obj.cover(gridOptionsTmp, props.grid)

  for (const column of gridOptionsTmp.columns!) {
    if (column.width || column.minWidth) continue
    if (column.type === 'seq') {
      column.width = 80
      column.fixed = 'left'
    } else {
      column.minWidth = 'auto'
    }
  }

  // 分页size
  if (props.grid.pagerConfig?.pageSizes) {
    gridOptionsTmp.pagerConfig!.pageSizes = props.grid.pagerConfig?.pageSizes
  }

  // 搜索表单
  if (!_hideSearch.value) {
    const searchFormOptions = {
      ...defaultSearchFormOptions,
    }
    obj.cover(searchFormOptions, props.searchForm)
    searchFormOptions.items = [
      {
        field: 'keyword',
        title: '关键字',
        span: 6,
        titleWidth: 70,
        itemRender: {
          name: mj_form_input,
          props: {
            clearable: true,
          },
        },
      },
      ...(props.searchForm?.items || []),
      {
        slots: { default: 'action' },
      },
    ]
    searchFormOptions.data = gridOptions.value.formConfig?.data || {}
    gridOptionsTmp.formConfig = searchFormOptions
  }

  if (props.hideToolbar) {
    gridOptionsTmp.toolbarConfig = undefined
  }

  gridOptions.value = gridOptionsTmp
}

// 监听配置
watch(
  [
    () => props.hideEdit,
    () => props.hideAdd,
    () => props.hideRemove,
    () => props.selectable,
    () => props.selectedItems,
    () => props.hideSearch,
  ],
  () => {
    initPermissions()
  },
  {
    immediate: true,
    // 深度监听
    deep: true,
  },
)

const updateSelectedStatus = () => {
  for (const row of _currentRows) {
    row[checkedProp] = false
  }
  if (_selectedItems.value?.items?.length) {
    // 设置默认选中
    for (const row of _currentRows) {
      for (const item of _selectedItems.value.items) {
        if (obj.isEqualIgnoreNumberString(row.id, item.id)) {
          row[checkedProp] = true
          break
        }
      }
    }
  }
}
watch(
  () => props.selectedItems,
  () => {
    updateSelectedStatus()
  },
  {
    immediate: true,
    // 深度监听
    deep: true,
  },
)

watch(
  [
    () => props.grid,
    // () => props.searchForm,
    () => _hideCheckbox.value,
    () => _hideEdit.value,
    () => _hideSearch.value,
    () => props.hideToolbar,
  ],
  () => {
    initGrid()
  },
  {
    immediate: true,
    // 深度监听
    deep: true,
  },
)

// 加载数据
async function loadPageData(params: any) {
  let res
  if (onlyLeftChecked.value) {
    res = {
      rows: _selectedItems.value?.items,
      totalRows: _selectedItems.value?.items?.length,
    }
  } else {
    const { page } = params
    const form = toRaw(gridOptions.value.formConfig?.data) || {}
    // 搜索条件
    let findPageParams = {
      ...props.searchParams,
      ...form,
    } as any
    if (props.grid.beforeLoadData) {
      findPageParams = props.grid.beforeLoadData(findPageParams)
    }
    if (findPageParams === mj_empty_data) {
      return {
        result: [],
        page: {
          total: 0,
        },
      }
    }
    res = await props.service.findPage(
      page.currentPage,
      page.pageSize,
      findPageParams,
    )
    if (props.grid.afterLoadData) {
      res = props.grid.afterLoadData(res)
    }
  }

  _currentRows = res.rows
  updateSelectedStatus()
  return {
    result: res.rows,
    page: {
      total: res.totalRows,
    },
  }
}
// 是否可以清除选中
const clearCheckboxDisable = ref(true)
const onlyLeftCheckedText = ref('仅显示选中')

// 是否可以删除
const removeDisable = ref(true)

// 删除
function remove() {
  const records = _selectedItems.value.items
  if (props.grid.beforeRemove && !props.grid.beforeRemove(records)) {
    // 不允许删除
    return
  }
  hud.confirmLoad(
    `是否要${props.deleteButtonText}【${records.length}】条数据？`,
    {
      task: async () => {
        await props.service.remove(records.map((r: any) => r.id))
        // 清除所有的选中
        clearAllCheckbox()
        reload()
        emit('dataModified', {
          action: mj_action_remove,
          data: toRaw(records),
        })
      },
      option: props.deleteButtonText,
    },
  )
}

// checkbox处理
function clearAllCheckbox() {
  gridRef.value.clearCheckboxRow()
  _selectedItems.value = {} as any
}

function toggleAllCheckboxEvent() {
  const oldData = gridRef.value.getCheckboxRecords()
  gridRef.value.toggleAllCheckboxRow()
  const newData = gridRef.value.getCheckboxRecords()
  tableCheckChange(oldData, newData)
}

function toggleCheckboxEvent(row: any) {
  if (props.multiSelectable) {
    const oldData = gridRef.value.getCheckboxRecords()
    gridRef.value.toggleCheckboxRow(row)
    const newData = gridRef.value.getCheckboxRecords()
    tableCheckChange(oldData, newData, row)
  } else {
    gridRef.value.toggleCheckboxRow(row)
    const len = gridRef.value.getCheckboxRecords().length
    // 单选
    _selectedItems.value = {
      item: row,
      items: len ? [row] : [],
    }
  }
}

// 增加选中的数据（items + data）
function union(items: any[], data: any[]) {
  const map = obj.list2map(items, 'id')
  for (const item of data) {
    if (!map[item.id]) {
      items.push(item)
    }
  }
  return items
}

// 移除选中的数据（items - data）
function sub(items: any[], data: any[]) {
  const map = obj.list2map(items, 'id')
  for (const item of data) {
    delete map[item.id]
  }
  return Object.values(map)
}

function tableCheckChange(oldRecords: any[], newRecords: any[], row?: any) {
  let items = _selectedItems.value?.items || []

  // eslint-disable-next-line unicorn/prefer-ternary
  if (newRecords.length > oldRecords.length) {
    // 增加
    items = union(items, newRecords)
  } else {
    // oldRecords - newRecords就是需要减掉的内容
    // 减少
    items = sub(items, sub(oldRecords, newRecords))
  }

  _selectedItems.value = {
    item: row,
    items,
  }
}

function reload() {
  gridRef.value.commitProxy('reload')
}

// 搜索表单重置
function searchFormReset() {
  // 清除所有选中
  clearAllCheckbox()

  const cfg = gridOptions.value.formConfig!
  const items = cfg.items!
  // 清空数据
  const oldData = toRaw(cfg.data) as any
  cfg.data = {}

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

  reload()

  const fn = props.grid.afterReset
  fn && fn()
}

// 编辑表单
function editFormDataModified(val: { action: number; data: any }) {
  reload()
  emit('dataModified', val)
}

function editFormVisibleChanged(visible: boolean, edit: boolean) {
  emit('editFormVisibleChanged', visible, edit)
}

const onlyLeftChecked = ref(false)
watch(
  () => onlyLeftChecked.value,
  () => {
    reload()
  },
)

watch(
  () => _selectedItems.value,
  (val) => {
    const len = val?.items?.length || 0
    if (len) {
      removeDisable.value = false
      onlyLeftCheckedText.value = `仅显示选中(${len})`
      clearCheckboxDisable.value = false
    } else {
      removeDisable.value = true
      onlyLeftCheckedText.value = '仅显示选中'
      clearCheckboxDisable.value = true
      onlyLeftChecked.value = false
    }
  },
  {
    immediate: true,
  },
)

defineExpose({
  getEditFormData() {
    return editFormRef.value?.getData()
  },
  add() {
    editFormRef.value?.add?.()
  },
  edit(row: any) {
    editFormRef.value?.edit(row)
  },
  hideEditFormItems(fields: any) {
    editFormRef.value?.hideItems(fields)
  },
  showEditFormItems(fields: any) {
    editFormRef.value?.showItems(fields)
  },
  getEditFormOptions() {
    return editFormRef.value?.getOptions()
  },
  update(data: any) {
    editFormRef.value?.update(data)
  },
  getSearchFormData() {
    return gridOptions.value.formConfig!.data
  },
  updateSearchFormData(data: any) {
    if (!data) return
    obj.cover(gridOptions.value.formConfig!.data, data)
  },
  reload,
  getGrid() {
    return gridRef.value
  },
})
</script>

<template>
  <div
    class="mj-table"
    :class="{
      selectable,
      'hide-search': _hideSearch,
      'hide-toolbar': hideToolbar,
      'hide-check-all': !multiSelectable,
    }"
  >
    <VxeGrid ref="gridRef" v-bind="gridOptions" v-on="gridEvents">
      <!-- 插槽传递给子组件 -->
      <template v-for="(_, slotName) in $slots" #[slotName]="slotProps">
        <slot
          v-if="
            slotName !== 'toolbarButtonsExtra' &&
            !(slotName as any).startsWith('form_')
          "
          :name="slotName"
          v-bind="slotProps || {}"
        ></slot>
      </template>

      <!-- 每一行的编辑按钮 -->
      <template #mj_edit_default="{ row }">
        <el-button
          :icon="Edit"
          :disabled="props.grid.disableEdit?.(row)"
          type="primary"
          @click.stop="editFormRef?.edit(row)"
        />
      </template>

      <!-- 表格顶部的按钮 -->
      <template #toolbarButtons>
        <slot name="toolbarButtonsExtra"></slot>
        <el-button
          v-if="!_hideAdd"
          type="primary"
          :icon="Plus"
          @click="editFormRef?.add"
        >
          {{ addButtonText }}
        </el-button>
        <el-button
          v-if="!_hideRemove"
          type="danger"
          :disabled="removeDisable"
          :icon="Delete"
          @click="remove"
        >
          {{ deleteButtonText }}
        </el-button>
        <template v-if="!_hideCheckbox">
          <el-button
            :disabled="clearCheckboxDisable"
            :icon="CloseBold"
            @click="clearAllCheckbox"
          >
            清除选中
          </el-button>
          <el-checkbox
            style="margin: 0 20px; color: #000"
            v-model="onlyLeftChecked"
            :disabled="clearCheckboxDisable"
          >
            {{ onlyLeftCheckedText }}
          </el-checkbox>
        </template>
      </template>

      <!-- 表头的checkbox -->
      <template #checkbox_header="{ checked, indeterminate }">
        <span class="custom-checkbox" @click.stop="toggleAllCheckboxEvent">
          <i v-if="indeterminate" class="vxe-icon-square-minus-fill"></i>
          <i v-else-if="checked" class="vxe-icon-square-checked-fill"></i>
          <i v-else class="vxe-icon-checkbox-unchecked"></i>
        </span>
      </template>

      <!-- 每一行的checkbox -->
      <template #checkbox_cell="{ row, checked, indeterminate }">
        <span class="custom-checkbox" @click.stop="toggleCheckboxEvent(row)">
          <i v-if="indeterminate" class="vxe-icon-square-minus-fill"></i>
          <i v-else-if="checked" class="vxe-icon-square-checked-fill"></i>
          <i v-else class="vxe-icon-checkbox-unchecked"></i>
        </span>
      </template>

      <template #action>
        <vxe-button icon="vxe-icon-repeat" @click="searchFormReset">
          重置
        </vxe-button>
        <vxe-button type="submit" icon="vxe-icon-search" status="primary">
          搜索
        </vxe-button>
      </template>
    </VxeGrid>

    <MjEditForm
      v-if="editForm"
      ref="editFormRef"
      :options="editForm"
      :service="service"
      @data-modified="editFormDataModified"
      @edit-form-visible-changed="editFormVisibleChanged"
    >
      <template v-for="(_, slotName) in $slots" #[slotName]="slotProps">
        <slot
          v-if="(slotName as any).startsWith('form_')"
          :name="slotName"
          v-bind="slotProps || {}"
        ></slot>
      </template>
    </MjEditForm>
  </div>
</template>

<style lang="scss" scoped>
@use './index.scss' as *;
</style>
