<template>
  <div
    class="workflow-node-component"
    :class="{
      selected: isSelected,
      'workflow-node--preview-right': hasPreview && previewRight && !useModernHeader,
      'workflow-node--split': useSplitLayout,
      'workflow-node--ramex': useModernHeader
    }"
  >
    <!-- 输入连接点 -->
    <Handle
      v-if="nodeCategory !== 'input'"
      id="input"
      type="target"
      :position="Position.Left"
      class="node-handle node-handle-input"
    />
    
    <!-- RamEx 参数节点：参考 Chart 编辑器 — 左侧黑底标题条 + 右侧预览文件名 -->
    <div v-if="useModernHeader" class="node-chrome-header">
      <div class="node-chrome-header__left">
        <div class="node-type-pill">
          <span class="node-type-pill__icon" aria-hidden="true" v-html="paletteIconSvg(ramexCategory)"></span>
          <span class="node-type-pill__label">{{ data.label || t('common.node') }}</span>
        </div>
      </div>
      <div class="node-chrome-header__right">
        <!-- <span v-if="hasPreview && previewFileLabel" class="node-preview-filename" :title="previewFileLabel">
          {{ previewFileLabel }}
        </span> -->
        <button class="delete-btn header-delete-btn delete-btn--chrome" :title="t('common.delete')" @click="handleDelete">
          <svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
            <path d="M18 6L6 18M6 6l12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
          </svg>
        </button>
      </div>
    </div>
    <!-- 其他演示节点：保留原彩色 header -->
    <div v-else class="node-header" :style="{ background: getHeaderColor() }">
      <div class="header-left">
        <div class="header-icon" v-html="getNodeIcon()"></div>
        <span class="header-title">{{ data.label || t('common.node') }}</span>
      </div>
      <div class="header-right">
        <span v-if="data.status === 'ready'" class="ready-badge">Ready</span>
        <button class="delete-btn header-delete-btn" :title="t('common.delete')" @click="handleDelete">
          <svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
            <path d="M18 6L6 18M6 6l12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
          </svg>
        </button>
        <button v-if="data.showSettings" class="settings-btn" @click.stop="handleSettings">
          <svg viewBox="0 0 24 24" fill="none">
            <circle cx="12" cy="12" r="3" stroke="currentColor" stroke-width="2"/>
            <path d="M12 1v6m0 6v6M5.64 5.64l4.24 4.24m4.24 4.24l4.24 4.24M1 12h6m6 0h6M5.64 18.36l4.24-4.24m4.24-4.24l4.24-4.24" stroke="currentColor" stroke-width="2"/>
          </svg>
        </button>
      </div>
    </div>
    
    <!-- 节点内容 -->
    <div
      class="node-body"
      :class="{
        'node-body--split': useSplitLayout,
        'node-body--preview-right': hasPreview && previewRight && !useModernHeader,
        'node-body--ramex': useModernHeader
      }"
    >
      <!-- 演示节点保留标签页；RamEx 节点不显示 Type/Data/Options -->
      <div v-if="!useModernHeader && data.tabs && data.tabs.length > 0" class="node-tabs">
        <button
          v-for="tab in data.tabs"
          :key="tab.id"
          class="tab-btn"
          :class="{ active: activeTab === tab.id }"
          @click.stop="activeTab = tab.id"
        >
          {{ tab.label }}
        </button>
      </div>

      <!-- 配置区（左侧参数） -->
      <div class="node-config" :class="{ 'node-config--modern': useModernHeader }">
        <!-- 输入数据类节点 -->
        <template v-if="nodeCategory === 'input'">
          <div class="field-group">
            <label class="field-label">FILE NAME:</label>
            <div class="file-input-wrapper">
              <svg class="file-icon" viewBox="0 0 24 24" fill="none">
                <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" stroke="currentColor" stroke-width="2"/>
                <polyline points="14,2 14,8 20,8" stroke="currentColor" stroke-width="2"/>
              </svg>
              <span class="file-name">{{ data.properties?.fileName || 'sales_data_2023.csv' }}</span>
            </div>
          </div>
          <div class="field-group">
            <label class="field-label">DELIMITER:</label>
            <select v-model="data.properties.delimiter" class="select-input" @change="updateProperty('delimiter', data.properties.delimiter)">
              <option value="comma">Comma (,)</option>
              <option value="semicolon">Semicolon (;)</option>
              <option value="tab">Tab</option>
            </select>
          </div>
        </template>
        
        <!-- 算法类节点 - Missing Values -->
        <template v-else-if="data.nodeType === 'missing-values'">
          <div v-if="activeTab === 'settings'" class="tab-content">
            <div class="field-group">
              <label class="field-label">STRATEGY:</label>
              <div class="strategy-buttons">
                <button
                  v-for="strategy in strategies"
                  :key="strategy.value"
                  class="strategy-btn"
                  :class="{ active: data.properties?.strategy === strategy.value }"
                  @click.stop="updateProperty('strategy', strategy.value)"
                >
                  <span v-html="strategy.icon" class="strategy-icon"></span>
                  <span>{{ strategy.label }}</span>
                </button>
              </div>
            </div>
            <div class="field-group">
              <label class="field-label">TARGET COLUMNS:</label>
              <select v-model="data.properties.targetColumns" class="select-input" @change="updateProperty('targetColumns', data.properties.targetColumns)">
                <option value="all-numeric">All Numeric</option>
                <option value="all">All</option>
                <option value="selected">Selected</option>
              </select>
            </div>
          </div>
          <div v-else class="tab-content">
            <div class="field-group">
              <label class="field-label">Advanced Settings:</label>
              <p class="field-hint">{{ t('editor.advancedHint') }}</p>
            </div>
          </div>
        </template>
        
        <!-- 算法类节点 - K-Means -->
        <template v-else-if="data.nodeType === 'kmeans'">
          <div class="field-group">
            <label class="field-label">No. of Clusters (K):</label>
            <input
              type="number"
              v-model.number="data.properties.k"
              class="number-input"
              min="1"
              @input="updateProperty('k', data.properties.k)"
            />
          </div>
          <div class="field-group">
            <label class="field-label">Max Iterations:</label>
            <div class="slider-wrapper">
              <input
                type="range"
                v-model.number="data.properties.maxIterations"
                class="slider-input"
                min="1"
                max="1000"
                step="1"
                @input="updateProperty('maxIterations', data.properties.maxIterations)"
              />
              <span class="slider-value">{{ data.properties.maxIterations || 300 }}</span>
            </div>
          </div>
          <div class="field-group">
            <label class="field-label">FEATURES:</label>
            <div class="features-list">
              <div
                v-for="(feature, index) in data.properties.features"
                :key="index"
                class="feature-item"
              >
                <span>{{ feature }}</span>
                <button class="remove-feature-btn" @click.stop="removeFeature(index)">
        <svg viewBox="0 0 24 24" fill="none">
          <path d="M18 6L6 18M6 6l12 12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
        </svg>
      </button>
    </div>
              <button class="add-feature-btn" @click.stop="addFeature">
                <svg viewBox="0 0 24 24" fill="none">
                  <path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
                </svg>
                + Add
              </button>
            </div>
          </div>
        </template>
        
        <!-- 显示类节点 - Distribution -->
        <template v-else-if="nodeCategory === 'display'">
          <div class="field-group">
            <label class="field-label">TARGET:</label>
            <select v-model="data.properties.target" class="select-input" @change="updateProperty('target', data.properties.target)">
              <option value="# Age_Distribution"># Age_Distribution</option>
            </select>
          </div>
          <div class="field-group">
            <label class="checkbox-label">
              <input
                type="checkbox"
                v-model="data.properties.normalizeHistogram"
                class="checkbox-input"
                @change="updateProperty('normalizeHistogram', data.properties.normalizeHistogram)"
              />
              <span>Normalize Histogram</span>
            </label>
        </div>
        </template>

        <!-- 通用表单渲染（用于预处理/分析参数节点） -->
        <template v-else-if="data.schema && data.schema.length">
          <div class="schema-form" :class="{ 'schema-form--modern': useModernHeader }">
            <section v-if="useModernHeader" class="config-section">
              <h4 class="config-section-title">{{ t('editor.params') }}</h4>
              <div
                v-for="field in data.schema"
                :key="field.key"
                class="config-row"
              >
                <label class="config-row-label">{{ field.label }}</label>
                <div class="config-row-control">
                  <input
                    v-if="field.type === 'number'"
                    type="number"
                    class="config-input config-input--compact"
                    :min="field.min"
                    :max="field.max"
                    :step="field.step ?? 1"
                    v-model.number="data.properties[field.key]"
                    @input="updateProperty(field.key, data.properties[field.key])"
                  />
                  <select
                    v-else-if="field.type === 'select'"
                    class="config-input"
                    v-model="data.properties[field.key]"
                    @change="updateProperty(field.key, data.properties[field.key])"
                  >
                    <option
                      v-for="opt in field.options ?? []"
                      :key="String(opt.value)"
                      :value="opt.value"
                    >
                      {{ opt.label }}
                    </option>
                  </select>
                  <label v-else-if="field.type === 'checkbox'" class="config-checkbox">
                    <input
                      type="checkbox"
                      class="checkbox-input"
                      v-model="data.properties[field.key]"
                      @change="updateProperty(field.key, data.properties[field.key])"
                    />
                    <span>{{ field.checkboxText ?? t('common.enabled') }}</span>
                  </label>
                  <div v-else-if="field.type === 'table'" class="config-table nodrag">
                    <table class="table-input">
                      <thead>
                        <tr>
                          <th v-for="col in field.columns ?? []" :key="col.key">{{ col.label }}</th>
                          <th class="table-input-action-col"></th>
                        </tr>
                      </thead>
                      <tbody>
                        <tr v-for="(row, rowIndex) in (data.properties[field.key] ?? [])" :key="rowIndex">
                          <td v-for="col in field.columns ?? []" :key="col.key">
                            <input
                              :type="col.type === 'number' ? 'number' : 'text'"
                              class="config-input"
                              :value="row[col.key]"
                              @input="updateTableCell(field, rowIndex, col, ($event.target as HTMLInputElement).value)"
                            />
                          </td>
                          <td class="table-input-action-col">
                            <button type="button" class="table-row-remove" :disabled="typeof field.minRows === 'number' && (data.properties[field.key] ?? []).length <= field.minRows" @click="removeTableRow(field, rowIndex)">×</button>
                          </td>
                        </tr>
                      </tbody>
                    </table>
                    <button type="button" class="table-row-add" @click="addTableRow(field)">+ {{ t('editor.addRow') }}</button>
                  </div>
                  <p v-if="field.hint" class="field-hint">{{ field.hint }}</p>
                </div>
              </div>
            </section>
            <template v-else>
              <div
                v-for="field in data.schema"
                :key="field.key"
                class="field-group"
              >
                <label class="field-label">{{ field.label }}</label>
                <input
                  v-if="field.type === 'number'"
                  type="number"
                  class="number-input"
                  :min="field.min"
                  :max="field.max"
                  :step="field.step ?? 1"
                  v-model.number="data.properties[field.key]"
                  @input="updateProperty(field.key, data.properties[field.key])"
                />
                <select
                  v-else-if="field.type === 'select'"
                  class="select-input"
                  v-model="data.properties[field.key]"
                  @change="updateProperty(field.key, data.properties[field.key])"
                >
                  <option
                    v-for="opt in field.options ?? []"
                    :key="String(opt.value)"
                    :value="opt.value"
                  >
                    {{ opt.label }}
                  </option>
                </select>
                <label v-else-if="field.type === 'checkbox'" class="checkbox-label">
                  <input
                    type="checkbox"
                    class="checkbox-input"
                    v-model="data.properties[field.key]"
                    @change="updateProperty(field.key, data.properties[field.key])"
                  />
                  <span>{{ field.checkboxText ?? '启用' }}</span>
                </label>
                <div v-else-if="field.type === 'table'" class="config-table nodrag">
                  <table class="table-input">
                    <thead>
                      <tr>
                        <th v-for="col in field.columns ?? []" :key="col.key">{{ col.label }}</th>
                        <th class="table-input-action-col"></th>
                      </tr>
                    </thead>
                    <tbody>
                      <tr v-for="(row, rowIndex) in (data.properties[field.key] ?? [])" :key="rowIndex">
                        <td v-for="col in field.columns ?? []" :key="col.key">
                          <input
                            :type="col.type === 'number' ? 'number' : 'text'"
                            class="config-input"
                            :value="row[col.key]"
                            @input="updateTableCell(field, rowIndex, col, ($event.target as HTMLInputElement).value)"
                          />
                        </td>
                        <td class="table-input-action-col">
                          <button type="button" class="table-row-remove" :disabled="typeof field.minRows === 'number' && (data.properties[field.key] ?? []).length <= field.minRows" @click="removeTableRow(field, rowIndex)">×</button>
                        </td>
                      </tr>
                    </tbody>
                  </table>
                  <button type="button" class="table-row-add" @click="addTableRow(field)">+ {{ t('editor.addRow') }}</button>
                </div>
                <p v-if="field.hint" class="field-hint">{{ field.hint }}</p>
              </div>
            </template>
          </div>
        </template>
      </div>

      <!-- 节点内结果预览（右侧图表区） -->
      <div v-if="hasPreview" class="node-preview" :class="{ 'node-preview--panel': useSplitLayout }">
        <div v-if="data.previewLoading" class="node-preview-loading">{{ t('editor.previewLoading') }}</div>
        <img
          v-else-if="data.previewUrl"
          class="node-preview-img"
          :src="data.previewUrl"
          :alt="t('editor.previewAlt', { label: data.label || t('common.node') })"
          loading="lazy"
        />
        <div v-else class="node-preview-skeleton" aria-hidden="true">
          <div class="node-preview-skeleton-shimmer"></div>
        </div>
      </div>
    </div>
    
    <!-- 输出连接点 -->
    <template v-if="data.outputs && data.outputs.length > 1">
      <Handle
        v-for="(output, index) in data.outputs"
        :key="output.id || index"
        :id="output.id || `output-${index}`"
        type="source"
        :position="Position.Right"
        :style="{ top: `${36 + index * 30}px` }"
        class="node-handle node-handle-output"
      />
    </template>
    <Handle
      v-else-if="nodeCategory !== 'display'"
      id="output"
      type="source"
      :position="Position.Right"
      class="node-handle node-handle-output"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { Handle, Position } from '@vue-flow/core'

const { t } = useI18n()
import type { NodeProps } from '@vue-flow/core'
import { paletteIconSvg, getPaletteCategoryFromNodeType, hasInlinePreviewCategory } from '../utils/paletteIcons'

interface NodeData {
  label: string
  nodeType?: 'local-csv' | 'missing-values' | 'distribution' | 'kmeans' | string
  properties: Record<string, any>
  schema?: Array<{
    key: string
    label: string
    type: 'number' | 'select' | 'checkbox' | 'table'
    hint?: string
    min?: number
    max?: number
    step?: number
    checkboxText?: string
    options?: Array<{ label: string; value: any }>
    columns?: Array<{ key: string; label: string; type: 'number' | 'text' }>
    minRows?: number
  }>
  headerColor?: string
  status?: 'ready' | 'pending' | 'error'
  showSettings?: boolean
  tabs?: Array<{ id: string; label: string }>
  outputs?: Array<{ id: string; label?: string }>
  previewUrl?: string
  previewLoading?: boolean
  previewFileName?: string
  paletteCategory?: string
}

interface Props extends NodeProps {
  data: NodeData
}

const props = defineProps<Props>()

const emit = defineEmits<{
  delete: [id: string]
  settings: [id: string]
  update: [data: Partial<NodeData>]
}>()

const isSelected = computed(() => props.selected || false)
const activeTab = ref(props.data.tabs?.[0]?.id || '')

const ramexCategory = computed(() => {
  const fromData = String(props.data.paletteCategory ?? '').trim()
  if (fromData) return fromData
  return getPaletteCategoryFromNodeType(String(props.data.nodeType || ''))
})

// 分析类节点（PCA / LDA 等）始终预留预览区：未运行前显示骨架占位，运行完成后回填实际图片
const hasPreview = computed(() =>
  hasInlinePreviewCategory(ramexCategory.value) ||
  Boolean(props.data.previewLoading) ||
  Boolean(String(props.data.previewUrl ?? '').trim())
)

const useModernHeader = computed(() => Boolean(ramexCategory.value))


const useSplitLayout = computed(() => useModernHeader.value && hasPreview.value)

const previewRight = computed(() => {
  if (useModernHeader.value) return hasPreview.value
  const schemaLen = props.data.schema?.length ?? 0
  return schemaLen >= 4
})

// 判断节点类别
const nodeCategory = computed(() => {
  if (props.data.nodeType === 'local-csv') return 'input'
  if (props.data.nodeType === 'distribution') return 'display'
  return 'algorithm'
})

// 节点类型对应的头部颜色
const getHeaderColor = (): string => {
  const colorMap: Record<string, string> = {
    'local-csv': '#3B82F6', // 蓝色
    'missing-values': '#F97316', // 橙色
    'distribution': '#10B981', // 绿色
    'kmeans': '#8B5CF6', // 紫色
  }
  return props.data.headerColor || colorMap[props.data.nodeType || ''] || '#6B7280'
}

// 获取节点图标
const getNodeIcon = (): string => {
  const iconMap: Record<string, string> = {
    'local-csv': DatabaseIconSvg,
    'missing-values': TriangleIconSvg,
    'distribution': BarChartIconSvg,
    'kmeans': TargetIconSvg,
  }
  return iconMap[props.data.nodeType || ''] || ''
}

// 初始化属性
const initializeNodeProperties = () => {
  // properties 在类型上为必填；这里做运行时兜底
  if (!props.data.properties) props.data.properties = {}

  // 如果带 schema 且 properties 缺省，则填充默认值（按当前值或合理默认）
  if (props.data.schema && props.data.schema.length > 0) {
    props.data.schema.forEach((f) => {
      if (props.data.properties && props.data.properties[f.key] !== undefined) return
      if (f.type === 'checkbox') props.data.properties![f.key] = false
      else if (f.type === 'number') props.data.properties![f.key] = typeof f.min === 'number' ? f.min : 0
      else if (f.type === 'select') props.data.properties![f.key] = f.options?.[0]?.value ?? ''
      else if (f.type === 'table') props.data.properties![f.key] = []
    })
  }
  
  if (props.data.nodeType === 'missing-values') {
    if (!props.data.properties.strategy) {
      props.data.properties.strategy = 'drop'
    }
    if (!props.data.properties.targetColumns) {
      props.data.properties.targetColumns = 'all-numeric'
    }
    if (!props.data.outputs) {
      props.data.outputs = [
        { id: 'output-1', label: 'Output 1' },
        { id: 'output-2', label: 'Output 2' }
      ]
    }
    if (!props.data.status) {
      props.data.status = 'ready'
    }
    if (!props.data.tabs) {
      props.data.tabs = [
        { id: 'settings', label: 'Settings' },
        { id: 'advanced', label: 'Advanced' }
      ]
      activeTab.value = 'settings'
    }
  }
  
  if (props.data.nodeType === 'distribution') {
    if (!props.data.properties.target) {
      props.data.properties.target = '# Age_Distribution'
    }
    if (props.data.properties.normalizeHistogram === undefined) {
      props.data.properties.normalizeHistogram = true
    }
  }
  
  if (props.data.nodeType === 'kmeans') {
    if (!props.data.properties.k) {
      props.data.properties.k = 3
    }
    if (!props.data.properties.maxIterations) {
      props.data.properties.maxIterations = 300
    }
    if (!props.data.properties.features) {
      props.data.properties.features = ['Age', 'Income', 'Score']
    }
    if (props.data.showSettings === undefined) {
      props.data.showSettings = true
    }
  }
  
  if (props.data.nodeType === 'local-csv') {
    if (!props.data.properties.fileName) {
      props.data.properties.fileName = 'sales_data_2023.csv'
    }
    if (!props.data.properties.delimiter) {
      props.data.properties.delimiter = 'comma'
    }
  }
}

// 初始化
initializeNodeProperties()

const updateProperty = (key: string, value: any) => {
  if (!props.data.properties) {
    props.data.properties = {}
  }
  props.data.properties[key] = value
  emit('update', { 
    properties: { ...props.data.properties },
    [key]: value
  })
}

const handleDelete = () => {
  emit('delete', props.id)
}

const handleSettings = () => {
  emit('settings', props.id)
}

const removeFeature = (index: number | string) => {
  const i = typeof index === 'number' ? index : Number(index)
  if (Number.isNaN(i)) return
  if (props.data.properties?.features) {
    props.data.properties.features.splice(i, 1)
    emit('update', { properties: { ...props.data.properties } })
  }
}

const addFeature = () => {
  if (!props.data.properties) {
    props.data.properties = {}
  }
  if (!props.data.properties.features) {
    props.data.properties.features = []
  }
  props.data.properties.features.push('New Feature')
  emit('update', { properties: { ...props.data.properties } })
}

type SchemaField = NonNullable<NodeData['schema']>[number]

const addTableRow = (field: SchemaField) => {
  if (!props.data.properties) props.data.properties = {}
  if (!Array.isArray(props.data.properties[field.key])) props.data.properties[field.key] = []
  const row: Record<string, any> = {}
  ;(field.columns ?? []).forEach((col) => {
    row[col.key] = col.type === 'number' ? 0 : ''
  })
  props.data.properties[field.key].push(row)
  updateProperty(field.key, props.data.properties[field.key])
}

const removeTableRow = (field: SchemaField, index: number) => {
  const rows = props.data.properties?.[field.key]
  if (!Array.isArray(rows)) return
  if (typeof field.minRows === 'number' && rows.length <= field.minRows) return
  rows.splice(index, 1)
  updateProperty(field.key, rows)
}

const updateTableCell = (field: SchemaField, index: number, column: { key: string; type: 'number' | 'text' }, value: string) => {
  const rows = props.data.properties?.[field.key]
  if (!Array.isArray(rows) || !rows[index]) return
  rows[index][column.key] = column.type === 'number' ? Number(value) : value
  updateProperty(field.key, rows)
}

// 图标SVG字符串
const DatabaseIconSvg = `
  <svg viewBox="0 0 24 24" fill="none" style="width: 16px; height: 16px;">
    <ellipse cx="12" cy="5" rx="9" ry="3" stroke="currentColor" stroke-width="2"/>
    <path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" stroke="currentColor" stroke-width="2"/>
    <path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" stroke="currentColor" stroke-width="2"/>
  </svg>
`

const TriangleIconSvg = `
  <svg viewBox="0 0 24 24" fill="none" style="width: 16px; height: 16px;">
    <path d="M12 5l-7 14h14L12 5z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
  </svg>
`

const BarChartIconSvg = `
  <svg viewBox="0 0 24 24" fill="none" style="width: 16px; height: 16px;">
    <rect x="3" y="3" width="18" height="18" rx="2" stroke="currentColor" stroke-width="2"/>
    <path d="M7 12h10M7 8h6M7 16h8" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
  </svg>
`

const TargetIconSvg = `
  <svg viewBox="0 0 24 24" fill="none" style="width: 16px; height: 16px;">
    <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
    <circle cx="12" cy="12" r="6" stroke="currentColor" stroke-width="2"/>
    <circle cx="12" cy="12" r="2" fill="currentColor"/>
  </svg>
`

const TrashIconSvg = `
  <svg viewBox="0 0 24 24" fill="none" style="width: 14px; height: 14px;">
    <path d="M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
  </svg>
`

const CalculatorIconSvg = `
  <svg viewBox="0 0 24 24" fill="none" style="width: 14px; height: 14px;">
    <rect x="4" y="2" width="16" height="20" rx="2" stroke="currentColor" stroke-width="2"/>
    <path d="M8 6h8M8 10h8M8 14h4M12 18h4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
  </svg>
`

const ZeroIconSvg = `
  <svg viewBox="0 0 24 24" fill="none" style="width: 14px; height: 14px;">
    <circle cx="12" cy="12" r="8" stroke="currentColor" stroke-width="2"/>
  </svg>
`

// Missing Values 策略选项（放在 SVG 常量之后，避免“先用后声明”告警）
const strategies = [
  { value: 'drop', label: 'Drop', icon: TrashIconSvg },
  { value: 'mean', label: 'Mean', icon: CalculatorIconSvg },
  { value: 'zero', label: 'Zero', icon: ZeroIconSvg }
]
</script>

<style scoped>
.workflow-node-component {
  --ramex-node-width: 340px;
  --ramex-split-width: 800px;
  --ramex-config-width: 300px;
  --ramex-header-height: 40px;
  --ramex-body-min-height: 168px;
  --ramex-split-body-height: 360px;

  background: #ffffff !important;
  border: none !important;
  border-radius: 0 !important;
  width: var(--ramex-node-width) !important;
  min-height: var(--ramex-body-min-height) !important;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !important;
  transition: all 0.2s ease !important;
  overflow: visible !important;
  position: relative;
  z-index: 1;
  cursor: default;
  display: flex;
  flex-direction: column;
  outline: none !important;
}

.workflow-node--preview-right,
.workflow-node--split {
  width: var(--ramex-split-width) !important;
  min-height: calc(var(--ramex-header-height) + var(--ramex-split-body-height)) !important;
}

.workflow-node--ramex:not(.workflow-node--split) {
  width: var(--ramex-node-width) !important;
  min-height: calc(var(--ramex-header-height) + var(--ramex-body-min-height)) !important;
}

.workflow-node-component:hover {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
}

.workflow-node-component.selected {
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18), 0 3px 6px rgba(0, 0, 0, 0.12) !important;
}

/* 头部样式 */
.node-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 12px;
  color: white;
  font-size: 13px;
  font-weight: 500;
  border-radius: 0;
  min-height: 36px;
}

/* RamEx：Chart 风格顶栏 */
.node-chrome-header {
  display: flex;
  align-items: stretch;
  min-height: var(--ramex-header-height);
  flex-shrink: 0;
  border-bottom: 1px solid #e5e7eb;
  background: #ffffff;
}

.node-chrome-header__left {
  display: flex;
  align-items: stretch;
  flex-shrink: 0;
}

.node-chrome-header__right {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  padding: 0 10px 0 12px;
  min-width: 0;
  background: #ffffff;
}

.node-type-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0 16px;
  background: #0f172a;
  color: #ffffff;
  font-size: 13px;
  font-weight: 600;
  min-height: var(--ramex-header-height);
}

.node-type-pill__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.node-type-pill__icon :deep(svg) {
  width: 16px;
  height: 16px;
  stroke: #ffffff !important;
}

.node-type-pill__icon :deep(svg [stroke]) {
  stroke: #ffffff !important;
}

.node-type-pill__icon :deep(svg [fill]:not([fill='none'])) {
  fill: #ffffff !important;
}

.node-type-pill__label {
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.node-preview-filename {
  font-size: 13px;
  font-weight: 600;
  color: #0f172a;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
  flex: 1;
  text-align: right;
}

.delete-btn--chrome {
  color: #64748b !important;
  opacity: 1 !important;
  flex-shrink: 0;
}

.delete-btn--chrome:hover {
  background: #f1f5f9 !important;
  color: #0f172a !important;
}

.header-left {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
}

.header-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  color: white;
}

.header-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 500;
}

.header-right {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

.ready-badge {
  background: #10b981;
  color: white;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  margin-right: 4px;
}

.delete-btn {
  width: 20px;
  height: 20px;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.2s ease;
  color: white;
  opacity: 0.8;
  flex-shrink: 0;
}

.delete-btn:hover {
  opacity: 1;
  background: rgba(255, 255, 255, 0.2);
}

.delete-btn svg {
  width: 14px;
  height: 14px;
}

.settings-btn {
  width: 20px;
  height: 20px;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.2s ease;
  color: white;
  opacity: 0.8;
  flex-shrink: 0;
}

.settings-btn:hover {
  opacity: 1;
  background: rgba(255, 255, 255, 0.2);
}

.settings-btn svg {
  width: 14px;
  height: 14px;
}

/* 节点内容 */
.node-body {
  background: #ffffff;
  border-radius: 0;
  padding: 12px;
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.node-body--ramex {
  background: #ffffff !important;
  padding: 0 !important;
}

.node-body--ramex:not(.node-body--split) .node-config--modern {
  padding: 14px 16px;
}

.workflow-node--ramex {
  border: 1px solid #cbd5e1 !important;
  border-radius: 2px !important;
}

.node-body--split,
.node-body--preview-right {
  flex-direction: row;
  align-items: stretch;
  gap: 0;
  min-height: var(--ramex-split-body-height);
  flex: 1;
}

.node-body--split .node-config,
.node-body--preview-right .node-config {
  flex: 0 0 var(--ramex-config-width);
  min-width: var(--ramex-config-width);
  max-width: var(--ramex-config-width);
  min-height: var(--ramex-split-body-height);
  padding: 14px 16px;
  border-right: 1px solid #e5e7eb;
  overflow-y: auto;
  align-self: stretch;
}

.node-config--modern {
  background: #ffffff;
}

/* 标签页 */
.node-tabs {
  display: flex;
  gap: 0;
  border-bottom: 1px solid #e5e7eb;
  margin-bottom: 12px;
  margin: -12px -12px 12px -12px;
  padding: 0 12px;
}

.tab-btn {
  padding: 8px 12px;
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  color: #6b7280;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
  margin-bottom: -1px;
}

.tab-btn:hover {
  color: #374151;
}

.tab-btn.active {
  color: #3B82F6;
  border-bottom-color: #3B82F6;
}

.node-config {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 12px;
}

.node-preview {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid #e5e7eb;
}

.node-preview--panel,
.node-body--preview-right .node-preview {
  margin-top: 0;
  padding: 16px;
  border-top: none;
  border-left: none;
  flex: 1 1 auto;
  min-width: 0;
  min-height: var(--ramex-split-body-height);
  display: flex;
  align-items: center;
  justify-content: center;
  background: #fafafa;
}

.node-body--split .node-preview--panel {
  align-self: stretch;
}

.node-preview-loading {
  font-size: 12px;
  color: #64748b;
}

.node-preview-skeleton {
  width: 100%;
  height: 100%;
  min-height: 120px;
  border-radius: 4px;
  background: #eef1f5;
  overflow: hidden;
  position: relative;
}

.node-preview--panel .node-preview-skeleton,
.node-body--preview-right .node-preview .node-preview-skeleton {
  min-height: calc(var(--ramex-split-body-height) - 32px);
}

.node-preview-skeleton-shimmer {
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.7), transparent);
  transform: translateX(-100%);
  animation: node-preview-shimmer 1.4s ease-in-out infinite;
}

@keyframes node-preview-shimmer {
  100% {
    transform: translateX(100%);
  }
}

.node-preview-img {
  width: 100%;
  max-height: 160px;
  object-fit: cover;
  border: 1px solid #e5e7eb;
  border-radius: 4px;
  background: #fff;
}

.node-body--preview-right .node-preview .node-preview-img,
.node-preview--panel .node-preview-img {
  width: auto;
  max-width: 100%;
  height: auto;
  max-height: calc(var(--ramex-split-body-height) - 32px);
  object-fit: contain;
  border: none;
  border-radius: 0;
  background: transparent;
}

.tab-content {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.field-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.field-label {
  font-size: 11px;
  font-weight: 600;
  color: #374151;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.field-hint {
  font-size: 12px;
  color: #9ca3af;
  font-style: italic;
}

/* 文件输入 */
.file-input-wrapper {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  background: #f9fafb;
  border: 1px solid #e5e7eb;
  border-radius: 4px;
  font-size: 13px;
  color: #374151;
}

.file-icon {
  width: 16px;
  height: 16px;
  color: #6b7280;
  flex-shrink: 0;
}

.file-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 下拉选择 */
.select-input {
  width: 100%;
  padding: 8px 12px;
  background: #ffffff;
  border: 1px solid #e5e7eb;
  border-radius: 4px;
  font-size: 13px;
  color: #374151;
  cursor: pointer;
  transition: all 0.2s ease;
}

.select-input:focus {
  outline: none;
  border-color: #3B82F6;
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}

/* 策略按钮组 */
.strategy-buttons {
  display: flex;
  gap: 8px;
}

.strategy-btn {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 10px 8px;
  background: #ffffff;
  border: 2px solid #e5e7eb;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.2s ease;
  font-size: 12px;
  font-weight: 500;
  color: #6b7280;
}

.strategy-btn:hover {
  border-color: #d1d5db;
  background: #f9fafb;
}

.strategy-btn.active {
  border-color: #3B82F6;
  background: var(--ramex-bg-subtle);
  color: #3B82F6;
}

.strategy-icon {
  display: flex;
  align-items: center;
  justify-content: center;
}

.strategy-icon svg {
  width: 18px;
  height: 18px;
  color: currentColor;
}

/* 复选框 */
.checkbox-label {
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-size: 13px;
  color: #374151;
}

.checkbox-input {
  width: 16px;
  height: 16px;
  cursor: pointer;
}

/* 通用 schema 表单 */
.schema-form {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.schema-form--modern {
  gap: 0;
}

.config-section {
  display: flex;
  flex-direction: column;
  gap: 0;
}

.config-section-title {
  margin: 0 0 10px;
  font-size: 11px;
  font-weight: 700;
  color: #64748b;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.config-row {
  display: grid;
  grid-template-columns: 112px 1fr;
  align-items: center;
  gap: 12px;
  padding: 8px 0;
  border-bottom: 1px solid #f1f5f9;
}

.config-row:last-child {
  border-bottom: none;
}

.config-row-label {
  font-size: 12px;
  font-weight: 500;
  color: #475569;
  line-height: 1.3;
  text-align: left;
}

.config-row-control {
  min-width: 0;
}

.config-input {
  width: 100%;
  padding: 5px 8px;
  font-size: 12px;
  color: #0f172a;
  background: #ffffff;
  border: 1px solid #d1d5db;
  border-radius: 2px;
  box-sizing: border-box;
}

.config-input--compact {
  max-width: 96px;
}

.config-input:focus {
  outline: none;
  border-color: #64748b;
}

.config-input[type='number']::-webkit-outer-spin-button,
.config-input[type='number']::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.config-input[type='number'] {
  -moz-appearance: textfield;
}

.config-checkbox {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: #334155;
  cursor: pointer;
}

/* 表格型属性(如波段注释表) */
.config-table {
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: 100%;
}

.table-input {
  width: 100%;
  border-collapse: collapse;
}

.table-input th {
  font-size: 10px;
  font-weight: 600;
  color: #64748b;
  text-align: left;
  padding: 2px 4px;
}

.table-input td {
  padding: 2px 4px;
}

.table-input td .config-input {
  padding: 3px 6px;
  font-size: 11px;
}

.table-input-action-col {
  width: 20px;
}

.table-row-remove {
  border: none;
  background: transparent;
  color: #94a3b8;
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
  padding: 2px 4px;
}

.table-row-remove:hover {
  color: #ef4444;
}

.table-row-add {
  align-self: flex-start;
  border: 1px dashed #cbd5e1;
  background: transparent;
  color: #64748b;
  font-size: 11px;
  border-radius: 3px;
  padding: 3px 8px;
  cursor: pointer;
}

.table-row-add:hover {
  border-color: #64748b;
  color: #334155;
}

/* 数字输入 */
.number-input {
  width: 100%;
  padding: 8px 12px;
  background: #ffffff;
  border: 1px solid #e5e7eb;
  border-radius: 4px;
  font-size: 13px;
  color: #374151;
  transition: all 0.2s ease;
}

.number-input:focus {
  outline: none;
  border-color: #3B82F6;
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}

.number-input::-webkit-outer-spin-button,
.number-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.number-input {
  -moz-appearance: textfield;
}

/* 滑块 */
.slider-wrapper {
  display: flex;
  align-items: center;
  gap: 12px;
}

.slider-input {
  flex: 1;
  height: 6px;
  background: #e5e7eb;
  border-radius: 3px;
  outline: none;
  cursor: pointer;
  -webkit-appearance: none;
}

.slider-input::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  background: #3B82F6;
  border-radius: 50%;
  cursor: pointer;
}

.slider-input::-moz-range-thumb {
  width: 16px;
  height: 16px;
  background: #3B82F6;
  border-radius: 50%;
  cursor: pointer;
  border: none;
}

.slider-value {
  font-size: 13px;
  font-weight: 500;
  color: #374151;
  min-width: 40px;
  text-align: right;
}

/* 特征列表 */
.features-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.feature-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 10px;
  background: #f9fafb;
  border: 1px solid #e5e7eb;
  border-radius: 4px;
  font-size: 13px;
  color: #374151;
}

.feature-item span {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.remove-feature-btn {
  width: 18px;
  height: 18px;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.2s ease;
  color: #ef4444;
  opacity: 0.6;
  flex-shrink: 0;
}

.remove-feature-btn:hover {
  opacity: 1;
  background: #fee2e2;
}

.remove-feature-btn svg {
  width: 12px;
  height: 12px;
}

.add-feature-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 8px 12px;
  background: #ffffff;
  border: 1px solid #e5e7eb;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 500;
  color: #6b7280;
  cursor: pointer;
  transition: all 0.2s ease;
  width: 100%;
}

.add-feature-btn:hover {
  border-color: #3B82F6;
  color: #3B82F6;
  background: var(--ramex-bg-subtle);
}

.add-feature-btn svg {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
}

/* 连接点样式 */
.node-handle {
  width: 12px !important;
  height: 12px !important;
  border: 2px solid white !important;
  border-radius: 50% !important;
  background: #6b7280 !important;
  cursor: crosshair !important;
  transition: all 0.2s ease !important;
}

.node-handle-input {
  left: -6px !important;
  background: #10b981 !important;
}

.node-handle-output {
  right: -6px !important;
  background: #3b82f6 !important;
}

.node-handle:hover {
  transform: scale(1.3) !important;
  box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.15) !important;
}
</style>
