<template>
  <div class="bitable-grid-scope">
    <vxe-grid
      ref="gridRef"
      :data="rows"
      :columns="gridColumns"
      :height="height"
      :loading="loading"
      :row-config="{ keyField: '_recordId' }"
      :column-config="{ resizable: true }"
      :virtual-y-config="{ enabled: true, gt: 60 }"
      :virtual-x-config="{ enabled: true, gt: 20 }"
      :edit-config="{
        trigger: 'click',
        mode: 'cell',
        showStatus: true,
        autoClear: false,
      }"
      @edit-closed="onEditClosed"
    >
      <template #empty>
        <a-empty :description="emptyText" />
      </template>
      <!-- Custom cell renderers for attachment/image fields (U6) -->
      <template
        v-for="f in attachmentFields"
        :key="f.id"
        #[`cell_${f.id}`]="{ row }"
      >
        <AttachmentCell
          v-if="f.field_type === 'attachment'"
          :files="(row[f.id] as IAttachmentMeta[] | null | undefined)"
        />
        <ImageCell
          v-else-if="f.field_type === 'image'"
          :images="(row[f.id] as IAttachmentMeta[] | null | undefined)"
        />
      </template>
      <!-- Column header dropdown menus (U4) -->
      <template
        v-for="f in fields"
        :key="`hdr_${f.id}`"
        #[`header_${f.id}`]
      >
        <ColumnHeaderMenu
          :field="f"
          @edit="emit('config-field', $event)"
          @hide="emit('hide-field', $event)"
          @delete="emit('delete-field', $event)"
        />
      </template>
      <!-- Select / Multiselect edit slots (U5) -->
      <template
        v-for="f in selectFields"
        :key="`edit_${f.id}`"
        #[`edit_${f.id}`]="{ row }"
      >
        <SelectCellEditor
          :model-value="(row[f.id] as string | string[] | null | undefined)"
          :options="(f.config.options as ISelectOption[] | string[] | undefined)"
          :multiple="f.field_type === 'multiselect'"
          @update:model-value="row[f.id] = $event"
        />
      </template>
      <!-- Select / Multiselect display slots (U5) -->
      <template
        v-for="f in selectFields"
        :key="`cell_sel_${f.id}`"
        #[`cell_sel_${f.id}`]="{ row }"
      >
        <SelectDisplay
          :value="(row[f.id] as string | string[] | null | undefined)"
          :options="(f.config.options as ISelectOption[] | string[] | undefined)"
          :multiple="f.field_type === 'multiselect'"
        />
      </template>
      <!-- Add-field column header -->
      <template #header__add_field>
        <div class="bitable-grid-scope__add-col" @click="emit('add-field')">
          <PlusOutlined /> 新增字段
        </div>
      </template>
    </vxe-grid>
  </div>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue'
import { VxeGrid } from 'vxe-table'
import { Empty as AEmpty } from 'ant-design-vue'
import { PlusOutlined } from '@ant-design/icons-vue'
import type { VxeGridProps, VxeGridEvents } from 'vxe-table'
import type {
  IBitableField,
  IBitableRecord,
  IAttachmentMeta,
  FieldType,
} from '@/api/bitable'
import AttachmentCell from './AttachmentCell.vue'
import ImageCell from './ImageCell.vue'
import ColumnHeaderMenu from './ColumnHeaderMenu.vue'
import SelectCellEditor from './SelectCellEditor.vue'
import SelectDisplay from './SelectDisplay.vue'
import type { ISelectOption } from './SelectCellEditor.vue'

type GridRow = Record<string, unknown> & { _rowId: string; _recordId: string }
type GridColumn = NonNullable<VxeGridProps['columns']>[number]

const props = withDefaults(
  defineProps<{
    fields: IBitableField[]
    records: IBitableRecord[]
    loading?: boolean
    height?: string | number
    emptyText?: string
  }>(),
  {
    loading: false,
    height: 'auto',
    emptyText: '暂无数据',
  },
)

const emit = defineEmits<{
  (e: 'edit-cell', payload: { recordId: string; fieldId: string; value: unknown }): void
  (e: 'add-field'): void
  (e: 'config-field', field: IBitableField): void
  (e: 'hide-field', fieldId: string): void
  (e: 'delete-field', field: IBitableField): void
}>()

const gridRef = ref<InstanceType<typeof VxeGrid> | null>(null)

// Fields that use custom slot renderers (attachment/image)
const attachmentFields = computed(() =>
  props.fields.filter(
    (f) => f.field_type === 'attachment' || f.field_type === 'image',
  ),
)

// Fields that use select/multiselect editors (U5)
const selectFields = computed(() =>
  props.fields.filter(
    (f) => f.field_type === 'select' || f.field_type === 'multiselect',
  ),
)

// Map records to grid rows with a stable _rowId (record.id)
const rows = computed<GridRow[]>(() =>
  props.records.map((r) => ({
    _rowId: r.id,
    _recordId: r.id,
    ...r.values,
  })),
)

// Build vxe-grid columns from field definitions
const gridColumns = computed<GridColumn[]>(() => {
  const cols: GridColumn[] = [
    {
      type: 'seq',
      width: 56,
      fixed: 'left',
      title: '#',
    },
  ]

  for (const f of props.fields) {
    cols.push(buildColumn(f))
  }

  // Add-field column (U4)
  cols.push({
    field: '__add_field',
    title: '',
    width: 120,
    fixed: 'right',
    resizable: false,
    editRender: { enabled: false },
    slots: { header: 'header__add_field' },
  })

  return cols
})

function buildColumn(f: IBitableField): GridColumn {
  const isFormula = f.field_type === 'formula'
  const isAttachment = f.field_type === 'attachment' || f.field_type === 'image'
  const base: GridColumn = {
    field: f.id,
    title: f.name,
    minWidth: 120,
    width: 160,
    resizable: true,
    showOverflow: 'tooltip',
    // Use header slot for column dropdown menu (U4)
    slots: { header: `header_${f.id}` },
  }

  // Formula fields are read-only
  if (isFormula) {
    return { ...base, editRender: { enabled: false } }
  }

  // Attachment/image fields use custom slot renderers, read-only
  if (isAttachment) {
    return {
      ...base,
      editRender: { enabled: false },
      slots: { ...base.slots, default: `cell_${f.id}` },
    }
  }

  // Editable cells by field type
  switch (f.field_type as FieldType) {
    case 'number':
      return {
        ...base,
        editRender: {
          enabled: true,
          name: 'VxeNumberInput',
        },
      }
    case 'date':
      return {
        ...base,
        editRender: {
          enabled: true,
          name: 'VxeDatePicker',
        },
      }
    case 'select':
    case 'multiselect':
      // U5: custom dropdown editor via slots
      return {
        ...base,
        editRender: { enabled: true },
        slots: {
          ...base.slots,
          edit: `edit_${f.id}`,
          default: `cell_sel_${f.id}`,
        },
      }
    default:
      return {
        ...base,
        editRender: { enabled: true, name: 'VxeInput' },
      }
  }
}

// Handle edit-closed event — emit cell update
const onEditClosed: VxeGridEvents.EditClosed = (params) => {
  const { row, column } = params
  const field = column.field
  if (!field) return
  const recordId = (row as GridRow)._recordId
  if (!recordId) return

  // Only emit if value actually changed (deep-equal for arrays / multiselect)
  const newValue = (row as Record<string, unknown>)[field]
  const original = props.records.find((r) => r.id === recordId)
  if (!original) return
  const oldValue = original.values[field]
  const sameValue =
    Array.isArray(newValue) && Array.isArray(oldValue)
      ? newValue.length === oldValue.length &&
        newValue.every((v, i) => v === oldValue[i])
      : newValue === oldValue
  if (sameValue) return

  emit('edit-cell', {
    recordId,
    fieldId: field,
    value: newValue,
  })
}

// Expose grid ref for parent (e.g. to refresh)
defineExpose({
  refresh: () => gridRef.value?.refreshColumn(),
})
</script>

<style scoped>
.bitable-grid-scope {
  width: 100%;
  height: 100%;
  overflow: hidden;
}

/* KTD10: CSS isolation — all vxe-table style overrides scoped to
   .bitable-grid-scope. Use :deep() to reach vxe-table internals. */
.bitable-grid-scope :deep(.vxe-table) {
  font-size: 13px;
}

.bitable-grid-scope :deep(.vxe-header--column) {
  background: var(--bg-secondary, #fafafa);
  font-weight: 600;
}

.bitable-grid-scope :deep(.vxe-body--column.is--dirty) {
  background: var(--bg-tertiary, #fffbe6);
}

.bitable-grid-scope :deep(.vxe-cell--dirty) {
  color: var(--color-primary, #1677ff);
}

.bitable-grid-scope__add-col {
  display: flex;
  align-items: center;
  gap: 4px;
  cursor: pointer;
  color: var(--text-secondary, #8c8c8c);
  font-size: 12px;
  padding: 0 8px;
  height: 100%;
  user-select: none;
}

.bitable-grid-scope__add-col:hover {
  color: var(--color-primary, #1677ff);
}
</style>
