<template>
  <a-drawer
    :open="open"
    :width="drawerWidth"
    :keyboard="true"
    :mask-closable="true"
    :body-style="{ padding: 0 }"
    placement="right"
    class="record-detail-drawer"
    :class="{ 'record-detail-drawer--wide': isWideDrawer }"
    @close="onClose"
  >
    <template #title>
      <span class="record-detail-drawer__title">
        {{ headerTitle }}
      </span>
    </template>

    <!-- Loading: skeleton (U1 LoadingState, Open Question P1) -->
    <LoadingState v-if="store.recordDetailLoading" :rows="6" :title="true" />

    <!-- 404: record not found / concurrently deleted -->
    <div
      v-else-if="store.recordDetailNotFound"
      class="record-detail-drawer__notfound"
    >
      <a-empty description="记录不存在或已被删除" />
      <a-button type="primary" @click="onClose">关闭</a-button>
    </div>

    <!-- Error: 5xx / network — ErrorState + retry (U1 ErrorState) -->
    <ErrorState
      v-else-if="store.recordDetailError"
      :message="store.recordDetailError"
      :retrying="store.recordDetailLoading"
      @retry="retry"
    />

    <!-- Ready: render fields -->
    <template v-else-if="record">
      <!-- Empty: 0 fields (Open Question P2) -->
      <div v-if="fields.length === 0" class="record-detail-drawer__empty">
        <a-empty description="该记录暂无字段数据" />
      </div>

      <template v-else>
        <!-- Sticky header: record title -->
        <div class="record-detail-drawer__header">
          <span class="record-detail-drawer__header-title">{{ recordTitle }}</span>
        </div>

        <!-- Body: vertical field list -->
        <a-form
          layout="vertical"
          class="record-detail-drawer__form"
          @submit.prevent="onSubmit"
        >
          <a-form-item
            v-for="f in fields"
            :key="f.id"
            class="record-detail-drawer__field"
          >
            <template #label>
              <span class="record-detail-drawer__field-label">
                <FieldTypeIcon :type="f.field_type" />
                {{ f.name }}
              </span>
            </template>
            <!-- Read-only renders -->
            <template v-if="!isFieldEditable(f)">
              <SelectDisplay
                v-if="f.field_type === 'select' || f.field_type === 'multiselect'"
                :value="(record.values[f.id] as string | string[] | null | undefined)"
                :options="(f.config.options as ISelectOption[] | string[] | undefined)"
                :multiple="f.field_type === 'multiselect'"
              />
              <AttachmentCell
                v-else-if="f.field_type === 'attachment'"
                :files="(record.values[f.id] as IAttachmentMeta[] | null | undefined)"
              />
              <ImageCell
                v-else-if="f.field_type === 'image'"
                :images="(record.values[f.id] as IAttachmentMeta[] | null | undefined)"
              />
              <span v-else class="record-detail-drawer__readonly">
                {{ formatFieldValue(record.values[f.id], f.field_type) }}
              </span>
              <span v-if="isAgentOwned(f)" class="record-detail-drawer__owner-tag">
                系统字段
              </span>
            </template>

            <!-- Editable inputs (user-owned, non-computed).
                 :value + @update:value with explicit casts — avoids `any`
                 while satisfying each component's prop type. -->
            <template v-else>
              <a-input
                v-if="f.field_type === 'text'"
                :value="(editBuffer[f.id] as string | undefined)"
                :disabled="submitting"
                placeholder="请输入"
                @update:value="editBuffer[f.id] = $event"
              />
              <a-input-number
                v-else-if="f.field_type === 'number'"
                :value="(editBuffer[f.id] as number | undefined)"
                :disabled="submitting"
                style="width: 100%"
                placeholder="请输入数字"
                @update:value="editBuffer[f.id] = $event"
              />
              <a-date-picker
                v-else-if="f.field_type === 'date'"
                :value="(editBuffer[f.id] as string | undefined)"
                value-format="YYYY-MM-DD"
                :disabled="submitting"
                style="width: 100%"
                placeholder="请选择日期"
                @update:value="editBuffer[f.id] = $event"
              />
              <a-select
                v-else-if="f.field_type === 'select'"
                :value="(editBuffer[f.id] as string | undefined)"
                :options="toSelectOptions(f.config.options)"
                :disabled="submitting"
                :allow-clear="true"
                placeholder="请选择"
                @update:value="editBuffer[f.id] = $event"
              />
              <a-select
                v-else-if="f.field_type === 'multiselect'"
                :value="(editBuffer[f.id] as string[] | undefined)"
                :options="toSelectOptions(f.config.options)"
                :disabled="submitting"
                :allow-clear="true"
                mode="multiple"
                placeholder="请选择"
                @update:value="editBuffer[f.id] = $event"
              />
            </template>
          </a-form-item>
        </a-form>
      </template>
    </template>

    <!-- Footer: save button (only when ready + has editable fields) -->
    <template v-if="showFooter" #footer>
      <div class="record-detail-drawer__footer">
        <a-button :disabled="submitting" @click="onClose">取消</a-button>
        <a-button
          type="primary"
          :loading="submitting"
          :disabled="submitting"
          @click="onSubmit"
        >
          保存
        </a-button>
      </div>
    </template>
  </a-drawer>
</template>

<script setup lang="ts">
import { computed, reactive, ref, watch } from 'vue'
import {
  Drawer as ADrawer,
  Form as AForm,
  FormItem as AFormItem,
  Input as AInput,
  InputNumber as AInputNumber,
  DatePicker as ADatePicker,
  Select as ASelect,
  Button as AButton,
  Empty as AEmpty,
} from 'ant-design-vue'
import type { IAttachmentMeta, IBitableField, IBitableRecord } from '@/api/bitable'
import { useBitableStore } from '@/stores/bitable'
import { useResponsiveBreakpoint } from '@/composables/useResponsiveBreakpoint'
import LoadingState from './LoadingState.vue'
import ErrorState from './ErrorState.vue'
import FieldTypeIcon from './FieldTypeIcon.vue'
import SelectDisplay from './SelectDisplay.vue'
import AttachmentCell from './AttachmentCell.vue'
import ImageCell from './ImageCell.vue'
import type { ISelectOption } from './SelectCellEditor.vue'
import {
  formatFieldValue,
  getDrawerWidth,
  getRecordTitle,
  isFieldEditable,
} from '@/helpers/recordDrawerUtils'

const props = defineProps<{
  /** Record id to display. When non-null + open, the drawer fetches the record. */
  recordId: string | null
  /** Open signal — typically `!!store.currentRecordId`. */
  open: boolean
}>()

const emit = defineEmits<{
  (e: 'close'): void
  (e: 'retry', recordId: string): void
}>()

const store = useBitableStore()
const { isMobile } = useResponsiveBreakpoint()

const submitting = ref(false)

// Edit buffer: keyed by field id, only populated for editable fields.
// Reset whenever the loaded record changes (watch below).
const editBuffer = reactive<Record<string, unknown>>({})

const record = computed<IBitableRecord | null>(() => store.currentRecord)
const fields = computed<IBitableField[]>(() => store.fields)

const isWideDrawer = computed(() => fields.value.length > 10)
const drawerWidth = computed(() => getDrawerWidth(fields.value.length, isMobile.value))

const recordTitle = computed(() =>
  record.value ? getRecordTitle(record.value, fields.value) : '记录详情',
)

const headerTitle = computed(() => {
  if (store.recordDetailLoading) return '加载中...'
  if (store.recordDetailNotFound) return '记录不存在'
  if (store.recordDetailError) return '加载失败'
  return recordTitle.value
})

const showFooter = computed(
  () =>
    !!record.value &&
    fields.value.length > 0 &&
    fields.value.some(isFieldEditable),
)

function isAgentOwned(f: IBitableField): boolean {
  return f.owner === 'agent'
}

function toSelectOptions(
  raw: unknown,
): { label: string; value: string }[] {
  if (!Array.isArray(raw)) return []
  return raw.map((opt) => {
    if (typeof opt === 'string') return { label: opt, value: opt }
    const o = opt as ISelectOption
    return { label: o.label ?? o.value, value: o.value }
  })
}

// Reset + hydrate the edit buffer when a new record loads.
watch(
  record,
  (rec) => {
    // Clear stale edits
    for (const k of Object.keys(editBuffer)) {
      delete editBuffer[k]
    }
    if (!rec) return
    for (const f of fields.value) {
      if (!isFieldEditable(f)) continue
      // Clone primitive values; clone arrays so multiselect edits don't
      // mutate the store's record.
      const v = rec.values[f.id]
      editBuffer[f.id] = Array.isArray(v) ? [...v] : (v ?? undefined)
    }
  },
  { immediate: true },
)

// If the drawer is re-opened for the same record id while the store still
// holds it, the watch above won't fire (record ref unchanged). Re-hydrate
// on open transition too.
watch(
  () => props.open,
  (isOpen) => {
    if (isOpen && record.value) {
      // Re-trigger hydration by reassigning via the record watch.
      for (const k of Object.keys(editBuffer)) delete editBuffer[k]
      for (const f of fields.value) {
        if (!isFieldEditable(f)) continue
        const v = record.value.values[f.id]
        editBuffer[f.id] = Array.isArray(v) ? [...v] : (v ?? undefined)
      }
    }
  },
)

function onClose(): void {
  emit('close')
}

function retry(): void {
  if (props.recordId) emit('retry', props.recordId)
}

async function onSubmit(): Promise<void> {
  if (!record.value || !props.recordId) return
  submitting.value = true
  try {
    // Only submit editable user-owned fields. Agent columns are merged back
    // from currentRecord.values inside updateRecordFields (preserves them).
    const edited: Record<string, unknown> = {}
    for (const f of fields.value) {
      if (!isFieldEditable(f)) continue
      if (f.id in editBuffer) edited[f.id] = editBuffer[f.id]
    }
    const ok = await store.updateRecordFields(props.recordId, edited)
    if (ok) {
      emit('close')
    }
  } finally {
    submitting.value = false
  }
}
</script>

<style scoped>
.record-detail-drawer__title {
  font-size: var(--bitable-font-md);
  font-weight: 600;
  color: var(--bitable-color-text);
}

.record-detail-drawer__header {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--bitable-color-bg);
  border-bottom: 1px solid var(--bitable-color-border);
  padding: var(--bitable-spacing-md) var(--bitable-spacing-lg);
}

.record-detail-drawer__header-title {
  font-size: var(--bitable-font-lg);
  font-weight: 600;
  color: var(--bitable-color-text);
  word-break: break-all;
}

.record-detail-drawer__form {
  padding: var(--bitable-spacing-md) var(--bitable-spacing-lg);
}

.record-detail-drawer__field {
  margin-bottom: var(--bitable-spacing-lg);
}

.record-detail-drawer__readonly {
  color: var(--bitable-color-text);
  word-break: break-word;
  white-space: pre-wrap;
}

.record-detail-drawer__owner-tag {
  display: inline-block;
  margin-left: var(--bitable-spacing-sm);
  padding: 0 var(--bitable-spacing-xs);
  font-size: var(--bitable-font-xs);
  color: var(--bitable-color-text-tertiary);
  background: var(--bitable-color-bg-tertiary);
  border-radius: var(--bitable-radius-sm);
}

.record-detail-drawer__notfound,
.record-detail-drawer__empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--bitable-spacing-md);
  padding: var(--bitable-spacing-xl) var(--bitable-spacing-lg);
}

.record-detail-drawer__footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--bitable-spacing-sm);
}
</style>
