<template>
  <a-form-item field="snmp_oids">
    <template #label>
      <div class="snmp-oid-label">
        <span>SNMP OID配置</span>
        <div class="snmp-oid-actions">
          <a-button size="mini" type="text" @click="switchEditMode">
            {{ editMode === 'table' ? 'JSON' : '表格' }}
          </a-button>
          <a-button v-if="editMode === 'table'" size="mini" type="primary" @click="addRow">添加</a-button>
        </div>
      </div>
    </template>

    <template v-if="editMode === 'table'">
      <a-table :data="rows" :pagination="false" size="small" class="snmp-oid-table">
        <template #columns>
          <a-table-column title="OID" data-index="oid" :width="220">
            <template #cell="{ record }">
              <a-input v-model="record.oid" @input="syncJsonFromRows" />
            </template>
          </a-table-column>
          <a-table-column title="指标名称" data-index="metric_name" :width="150">
            <template #cell="{ record }">
              <a-input v-model="record.metric_name" @input="syncJsonFromRows" />
            </template>
          </a-table-column>
          <a-table-column title="指标unit" data-index="metric_unit" :width="120">
            <template #cell="{ record }">
              <a-input v-model="record.metric_unit" @input="syncJsonFromRows" />
            </template>
          </a-table-column>
          <a-table-column title="类型" data-index="type" :width="100">
            <template #cell="{ record }">
              <a-input v-model="record.type" @input="syncJsonFromRows" />
            </template>
          </a-table-column>
          <a-table-column title="操作" :width="80">
            <template #cell="{ rowIndex }">
              <a-button size="mini" type="text" status="danger" @click="removeRow(rowIndex)">删除</a-button>
            </template>
          </a-table-column>
        </template>
      </a-table>
    </template>

    <a-textarea
      v-else
      v-model="jsonText"
      :rows="5"
      placeholder='可留空使用默认模板，或填写如 [{"oid":"1.3.6.1.2.1.1.3.0","metric_name":"sys_uptime"}]'
      @input="emitJsonText"
    />
  </a-form-item>
</template>

<script setup lang="ts">
import { Message } from '@arco-design/web-vue'
import { onMounted, ref, watch } from 'vue'

interface Props {
  modelValue?: string
  resetKey?: number
}

type EditMode = 'table' | 'json'

interface SnmpOidRow {
  oid: string
  metric_name: string
  metric_unit: string
  type: string
}

type JsonRecord = Record<string, unknown>

const props = defineProps<Props>()

const emit = defineEmits<{
  (event: 'update:modelValue', value: string): void
}>()

const tableFields = ['oid', 'metric_name', 'metric_unit', 'type']
const editMode = ref<EditMode>('table')
const rows = ref<SnmpOidRow[]>([])
const jsonText = ref('')

const createEmptyRow = (): SnmpOidRow => ({
  oid: '',
  metric_name: '',
  metric_unit: '',
  type: '',
})

const isJsonRecord = (value: unknown): value is JsonRecord =>
  !!value && typeof value === 'object' && !Array.isArray(value)

const parseJsonArray = (raw: string): unknown[] => {
  const text = raw.trim()
  if (!text) {
    return []
  }

  const parsed = JSON.parse(text)
  if (!Array.isArray(parsed)) {
    throw new Error('SNMP OID 配置必须是合法 JSON 数组')
  }
  return parsed
}

const hasUnsupportedFields = (items: unknown[]) =>
  items.some((item) => {
    if (!isJsonRecord(item)) {
      return true
    }
    return Object.keys(item).some((key) => !tableFields.includes(key))
  })

const normalizeRows = (sourceRows: SnmpOidRow[]) =>
  sourceRows
    .map((row) => ({
      oid: row.oid.trim(),
      metric_name: row.metric_name.trim(),
      metric_unit: row.metric_unit.trim(),
      type: row.type.trim(),
    }))
    .filter((row) => row.oid || row.metric_name || row.metric_unit || row.type)

const findIncompleteRowIndex = (sourceRows: SnmpOidRow[]) =>
  sourceRows.findIndex((row) => {
    const normalizedRow = {
      oid: row.oid.trim(),
      metric_name: row.metric_name.trim(),
      metric_unit: row.metric_unit.trim(),
      type: row.type.trim(),
    }
    const hasAnyValue =
      normalizedRow.oid || normalizedRow.metric_name || normalizedRow.metric_unit || normalizedRow.type
    return !!hasAnyValue && (!normalizedRow.oid || !normalizedRow.metric_name)
  })

const findInvalidJsonRowIndex = (items: unknown[]) =>
  items.findIndex((item) => {
    if (!isJsonRecord(item)) {
      return true
    }
    return (
      typeof item.oid !== 'string' ||
      typeof item.metric_name !== 'string' ||
      !item.oid.trim() ||
      !item.metric_name.trim()
    )
  })

const normalizeJsonItems = (items: unknown[]) =>
  items.map((item) => {
    const record = item as JsonRecord
    return {
      ...record,
      oid: String(record.oid ?? '').trim(),
      metric_name: String(record.metric_name ?? '').trim(),
      metric_unit: typeof record.metric_unit === 'string' ? record.metric_unit.trim() : record.metric_unit,
      type: typeof record.type === 'string' ? record.type.trim() : record.type,
    }
  })

const parseRows = (raw: string): SnmpOidRow[] =>
  parseJsonArray(raw).map((item) => {
    const record = isJsonRecord(item) ? item : {}
    return {
      oid: typeof record.oid === 'string' ? record.oid.trim() : '',
      metric_name: typeof record.metric_name === 'string' ? record.metric_name.trim() : '',
      metric_unit: typeof record.metric_unit === 'string' ? record.metric_unit.trim() : '',
      type: typeof record.type === 'string' ? record.type.trim() : '',
    }
  })

const stringifyRows = (sourceRows: SnmpOidRow[]) => {
  const normalizedRows = normalizeRows(sourceRows)
  return normalizedRows.length ? JSON.stringify(normalizedRows, null, 2) : ''
}

const emitValue = (value: string) => {
  jsonText.value = value
  emit('update:modelValue', value)
}

const syncRowsFromJson = () => {
  rows.value = parseRows(jsonText.value)
}

const syncJsonFromRows = () => {
  emitValue(stringifyRows(rows.value))
}

const emitJsonText = () => {
  emit('update:modelValue', jsonText.value)
}

const addRow = () => {
  if (editMode.value !== 'table') {
    return
  }

  rows.value.push(createEmptyRow())
  syncJsonFromRows()
}

const removeRow = (index: number) => {
  rows.value.splice(index, 1)
  syncJsonFromRows()
}

const switchEditMode = () => {
  if (editMode.value === 'table') {
    syncJsonFromRows()
    editMode.value = 'json'
    return
  }

  let items: unknown[]
  try {
    items = parseJsonArray(jsonText.value)
  } catch {
    Message.warning('SNMP OID 配置必须是合法 JSON 数组，修正后才能切换到表格模式')
    return
  }

  if (hasUnsupportedFields(items)) {
    Message.warning('当前 SNMP OID JSON 包含表格不支持的字段，请在 JSON 模式下编辑')
    return
  }

  syncRowsFromJson()
  editMode.value = 'table'
}

const validate = () => {
  if (editMode.value === 'table') {
    const invalidIndex = findIncompleteRowIndex(rows.value)
    if (invalidIndex >= 0) {
      Message.warning(`SNMP OID 第 ${invalidIndex + 1} 行请填写 OID 和指标名称`)
      return false
    }

    syncJsonFromRows()
    return true
  }

  if (!jsonText.value.trim()) {
    rows.value = []
    emitValue('')
    return true
  }

  let parsedItems: unknown[]
  try {
    parsedItems = parseJsonArray(jsonText.value)
  } catch {
    Message.warning('SNMP OID 配置必须是合法 JSON 数组')
    return false
  }

  const invalidIndex = findInvalidJsonRowIndex(parsedItems)
  if (invalidIndex >= 0) {
    Message.warning(`SNMP OID 第 ${invalidIndex + 1} 行请填写 OID 和指标名称`)
    return false
  }

  const normalizedItems = normalizeJsonItems(parsedItems)
  rows.value = parseRows(JSON.stringify(normalizedItems))
  emitValue(normalizedItems.length ? JSON.stringify(normalizedItems, null, 2) : '')
  return true
}

const resetFromModelValue = () => {
  jsonText.value = props.modelValue ?? ''

  if (!jsonText.value.trim()) {
    editMode.value = 'table'
    rows.value = []
    return
  }

  try {
    const items = parseJsonArray(jsonText.value)
    if (hasUnsupportedFields(items)) {
      editMode.value = 'json'
      rows.value = []
      return
    }

    rows.value = parseRows(jsonText.value)
    editMode.value = 'table'
  } catch {
    editMode.value = 'json'
    rows.value = []
  }
}

watch(
  () => props.modelValue,
  (value) => {
    if ((value ?? '') !== jsonText.value) {
      resetFromModelValue()
    }
  }
)

watch(
  () => props.resetKey,
  () => {
    resetFromModelValue()
  }
)

onMounted(() => {
  resetFromModelValue()
})

defineExpose({
  validate,
  resetFromModelValue,
})
</script>

<style scoped>
.snmp-oid-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
}

.snmp-oid-actions {
  display: flex;
  gap: 8px;
}

.snmp-oid-table {
  width: 100%;
}
</style>
