import React, { useEffect, useState } from 'react';
import {
  Button,
  Checkbox,
  Input,
  InputNumber,
  message,
  Segmented,
  Select,
  Space,
  Spin,
  Switch,
  Tag,
  Tooltip,
  Typography,
} from 'antd';
import {
  DeleteOutlined,
  DisconnectOutlined,
  LinkOutlined,
  MinusCircleOutlined,
  PlusOutlined,
  SendOutlined,
} from '@ant-design/icons';
import { customRequest } from 'helpers/http';
import type { WorkflowNode, WorkflowNodeType } from '../types';

const { Text, Title } = Typography;
const { TextArea } = Input;

const CATEGORY_COLORS: Record<string, { bg: string; border: string }> = {
  TRIGGER: { bg: '#e6f7ff', border: '#91caff' },
  ACTION: { bg: '#f6ffed', border: '#b7eb8f' },
  CONDITION: { bg: '#fff7e6', border: '#ffd591' },
  LOOP: { bg: '#f9f0ff', border: '#d3adf7' },
  FLOW_CONTROL: { bg: '#f0f5ff', border: '#adc6ff' },
  APPROVAL: { bg: '#fff1f0', border: '#ffa39e' },
  SUB_WORKFLOW: { bg: '#f0f5ff', border: '#adc6ff' },
};

const CATEGORY_LABELS: Record<string, string> = {
  TRIGGER: '触发器',
  ACTION: '动作',
  CONDITION: '条件',
  LOOP: '循环',
  FLOW_CONTROL: '流程控制',
  APPROVAL: '审批',
  SUB_WORKFLOW: '子流程',
};

// Fields that should use TextArea for longer input
const TEXTAREA_FIELDS = new Set([
  'query',
  'message',
  'condition',
  'body',
  'headers',
  'inputMapping',
  'script',
  'rawSql',
  'data',
  'responseBody',
  'fallbackExpression',
]);

// Rich tooltip content for node types (hardcoded)
const NODE_TOOLTIPS: Record<string, string> = {
  cron_trigger:
    '定时触发工作流\n' +
    '━━━━━━━━━━━━━━━━━━━━\n' +
    '示例: 每天零点生成日报、每5分钟检查队列、每月1号结算\n' +
    'Temporal: ScheduleClient 创建定时 Schedule，到点启动 Workflow',
  event_trigger:
    '监听内部业务事件，事件发生时自动触发\n' +
    '━━━━━━━━━━━━━━━━━━━━\n' +
    '事件类别:\n' +
    '• 数据变更: record.created, record.updated\n' +
    '• 状态变更: order.paid, ticket.escalated\n' +
    '• 用户行为: user.registered, login_failed_3x\n' +
    '• 系统事件: deployment.completed, health_check.failed\n' +
    '━━━━━━━━━━━━━━━━━━━━\n' +
    'Temporal: 事件总线收到事件后调用 WorkflowClient.start() 启动新 Workflow，或通过 Signal 唤醒等待中的 Workflow\n\n' +
    '通常后端会有一个事件总线（如 Redis Pub/Sub、NATS、Kafka、或简单的内存 EventEmitter），当业务代码 emit("order.paid", payload)时，事件中间件查找所有订阅了该事件的 active workflow 定义，并启动对应的 Temporal Workflow。',
  webhook_trigger:
    '通过 HTTP 请求触发工作流\n' +
    '━━━━━━━━━━━━━━━━━━━━\n' +
    '示例: 第三方支付回调、GitHub Webhook、表单提交\n' +
    'Temporal: HTTP 网关接收请求后调用 WorkflowClient.start()，支持同步等待结果或异步返回',
  manual_trigger:
    '手动触发，用于调试或一次性执行\n' +
    '━━━━━━━━━━━━━━━━━━━━\n' +
    '示例: 手动跑数据迁移、调试新 Workflow、一次性批处理\n' +
    'Temporal: 直接调用 WorkflowClient.start() 并传入自定义 input',
  http_request:
    '向外部 API 发送 HTTP 请求\n' +
    '━━━━━━━━━━━━━━━━━━━━\n' +
    '示例: 调用第三方 API、推送数据到下游系统、获取外部数据\n' +
    'Temporal: 封装为 Activity，自带超时和重试策略',
  db_query:
    '执行数据库查询（读操作）\n' +
    '━━━━━━━━━━━━━━━━━━━━\n' +
    '示例: 查询订单状态、获取用户列表、统计聚合\n' +
    'Temporal: 封装为 Activity，支持参数化查询防注入',
  db_execute:
    '执行数据库写操作\n' +
    '━━━━━━━━━━━━━━━━━━━━\n' +
    '示例: 插入审批记录、更新订单状态、批量删除过期数据\n' +
    'Temporal: 封装为 Activity，失败时 Workflow 可选择重试或补偿',
  send_notification:
    '发送通知消息\n' +
    '━━━━━━━━━━━━━━━━━━━━\n' +
    '示例: 审批结果邮件、Slack 告警、企微/钉钉群消息\n' +
    'Temporal: 封装为 Activity，支持模板变量和多渠道',
  code:
    '执行自定义 JavaScript 代码\n' +
    '━━━━━━━━━━━━━━━━━━━━\n' +
    '示例: 数据格式转换、复杂计算、JSON 结构重组、条件逻辑\n' +
    'Temporal: 封装为 Activity，在 Worker 中通过 VM2 沙箱执行',
  set_variable:
    '设置、转换或重命名工作流变量\n' +
    '━━━━━━━━━━━━━━━━━━━━\n' +
    '示例: 提取嵌套字段、重命名 API 返回字段、构造下游所需结构\n' +
    'Temporal: 在 Workflow 代码中直接操作 context 对象',
  if_else:
    '根据条件走不同分支\n' +
    '━━━━━━━━━━━━━━━━━━━━\n' +
    '示例: 金额>1万走人工审批、VIP用户走快速通道\n' +
    'Temporal: Workflow 代码中的 if/else 控制流',
  switch:
    '根据字段值走多路分支\n' +
    '━━━━━━━━━━━━━━━━━━━━\n' +
    '示例: 按订单类型分发、按地区选择处理逻辑\n' +
    'Temporal: Workflow 代码中的 switch/case 控制流',
  for_each:
    '遍历数组，对每个元素执行子流程\n' +
    '━━━━━━━━━━━━━━━━━━━━\n' +
    '示例: 批量发送通知、逐个处理订单项、并行调用多个 API\n' +
    'Temporal: 循环内启动 Activity 或 Child Workflow，支持并行控制',
  delay:
    '暂停工作流执行，等待指定时间或信号\n' +
    '━━━━━━━━━━━━━━━━━━━━\n' +
    '示例: 等待30分钟后发提醒、等到指定时间执行、等待外部回调\n' +
    'Temporal: workflow.sleep()（持久化定时器，服务重启不丢失）',
  parallel:
    '并行执行多个分支，等待完成后合并\n' +
    '━━━━━━━━━━━━━━━━━━━━\n' +
    '示例: 同时调用多个 API 聚合结果、并行审批多个部门\n' +
    'Temporal: Promise.all() 并行启动多个 Activity/Child Workflow',
  error_handler:
    '捕获上游节点错误，执行降级逻辑\n' +
    '━━━━━━━━━━━━━━━━━━━━\n' +
    '示例: API 失败后走备用方案、超时后发告警通知\n' +
    'Temporal: try/catch + RetryPolicy（支持固定/指数退避重试）',
  approval:
    '暂停流程等待人工审批\n' +
    '━━━━━━━━━━━━━━━━━━━━\n' +
    '示例: 费用报销审批、上线发布审批、权限申请审批\n' +
    'Temporal: workflow.condition() 等待 Signal，审批人通过 SignalWorkflow 发送结果',
  sub_workflow:
    '调用另一个已定义的工作流作为子流程\n' +
    '━━━━━━━━━━━━━━━━━━━━\n' +
    '示例: 复用通用审批流程、嵌套调用数据处理 Pipeline\n' +
    'Temporal: executeChild() 启动 Child Workflow，支持同步等待或异步触发',
};

// Placeholder hints for specific fields
const FIELD_PLACEHOLDERS: Record<string, string> = {
  cron: '0 0 * * * (每天零点)',
  timezone: 'Asia/Shanghai',
  eventName: 'order.created',
  path: '/api/webhook/my-hook',
  url: 'https://api.example.com/endpoint',
  query: 'SELECT * FROM table WHERE id = $1',
  rawSql: 'INSERT INTO table (col) VALUES ($1)',
  condition: 'data.amount > 1000',
  fallbackExpression: 'data.status === "active"',
  iteratorField: 'data.items',
  message: '通知内容，支持 {{variable}} 模板变量',
  subject: '邮件主题',
  field: 'data.status',
  timeout: '超时时间（秒）',
  duration: '延迟秒数',
  script: '// 在此编写 JavaScript 代码\nreturn { result: input.data };',
  table: '表名',
  where: 'id = $1',
  datasource: 'default',
  signalName: 'approval_signal',
  gotoNodeId: '目标节点 ID',
};

interface Props {
  node: WorkflowNode | null;
  nodeTypes: WorkflowNodeType[];
  currentWorkflowId?: string;
  onSave: (
    nodeId: string,
    updates: {
      label?: string;
      config?: Record<string, any>;
      instanceRef?: WorkflowNode['instanceRef'] | null;
    },
  ) => void;
  onLabelChange: (nodeId: string, label: string) => void;
  onAiEdit: (nodeId: string, prompt: string) => void;
  onDelete?: (nodeId: string) => void;
  onAbort?: () => void;
  aiLoading: boolean;
}

export function NodePropertyPanel({
  node,
  nodeTypes,
  currentWorkflowId,
  onSave,
  onLabelChange,
  onAiEdit,
  onDelete,
  onAbort,
  aiLoading,
}: Props) {
  const [label, setLabel] = useState('');
  const [config, setConfig] = useState<Record<string, any>>({});
  const [prompt, setPrompt] = useState('');
  const [dirty, setDirty] = useState(false);
  const [activeTab, setActiveTab] = useState<'config' | 'ai'>('config');
  const [workflowOptions, setWorkflowOptions] = useState<
    { label: string; value: string }[]
  >([]);
  const [instances, setInstances] = useState<
    {
      id: string;
      name: string;
      description: string | null;
      config: Record<string, any>;
    }[]
  >([]);
  const [selectedInstanceId, setSelectedInstanceId] = useState<string | null>(
    null,
  );
  const [instanceLoading, setInstanceLoading] = useState(false);
  const [newInstanceName, setNewInstanceName] = useState('');
  const [newInstanceDesc, setNewInstanceDesc] = useState('');
  const [saveAsLoading, setSaveAsLoading] = useState(false);
  const [alsoSaveAsInstance, setAlsoSaveAsInstance] = useState(false);

  // Find configSchema for current node type
  const currentNodeType = nodeTypes.find((nt) => nt.type === node?.type);
  const configSchema = (currentNodeType?.configSchema || {}) as any;
  const requiredFields: string[] = configSchema.required || [];
  const properties: Record<string, any> = configSchema.properties || {};

  useEffect(() => {
    if (node) {
      setLabel(node.label);
      // Merge existing config with schema defaults
      const mergedConfig: Record<string, any> = {};
      for (const [key, prop] of Object.entries<any>(properties)) {
        const existingValue = node.config?.[key];
        if (existingValue !== undefined) {
          mergedConfig[key] = existingValue;
        } else if (prop.type === 'string') {
          mergedConfig[key] = '';
        } else if (prop.type === 'number') {
          mergedConfig[key] = undefined;
        } else if (prop.type === 'boolean') {
          mergedConfig[key] = false;
        } else if (prop.type === 'array') {
          mergedConfig[key] = [];
        } else if (prop.type === 'object') {
          mergedConfig[key] = {};
        }
      }
      setConfig(mergedConfig);
      setDirty(false);
      setPrompt('');
    }
  }, [node?.id, currentNodeType]);

  // Sync form when node config/label updates externally (e.g. AI edit)
  useEffect(() => {
    if (!node || dirty) return;
    setLabel(node.label);
    const mergedConfig: Record<string, any> = {};
    for (const [key, prop] of Object.entries<any>(properties)) {
      const existingValue = node.config?.[key];
      if (existingValue !== undefined) {
        mergedConfig[key] = existingValue;
      } else if (prop.type === 'string') {
        mergedConfig[key] = '';
      } else if (prop.type === 'number') {
        mergedConfig[key] = undefined;
      } else if (prop.type === 'boolean') {
        mergedConfig[key] = false;
      } else if (prop.type === 'array') {
        mergedConfig[key] = [];
      } else if (prop.type === 'object') {
        mergedConfig[key] = {};
      }
    }
    setConfig(mergedConfig);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [node?.label, JSON.stringify(node?.config)]);

  // Load workflow list for Sub Workflow node
  useEffect(() => {
    if (node?.type === 'sub_workflow') {
      customRequest<{ data: any[] }>('workflow', 'GET')
        .then((res) => {
          const list = Array.isArray(res) ? res : res?.data || [];
          setWorkflowOptions(
            list
              .filter((w: any) => String(w.id) !== currentWorkflowId)
              .map((w: any) => ({ label: w.name, value: String(w.id) })),
          );
        })
        .catch(() => {});
    }
  }, [node?.type, currentWorkflowId]);

  // Load instances for current node type
  useEffect(() => {
    if (!currentNodeType?.id) {
      setInstances([]);
      return;
    }
    setInstanceLoading(true);
    customRequest<{ data: any[] }>('workflowNodeInstance/findMany', 'POST', {
      where: { nodeTypeId: Number(currentNodeType.id), isEnabled: true },
      take: 100,
    })
      .then((res) => {
        const list = Array.isArray(res) ? res : res?.data || [];
        setInstances(
          list.map((item: any) => ({
            id: String(item.id),
            name: item.name,
            description: item.description,
            config: item.config ?? {},
          })),
        );
      })
      .catch(() => setInstances([]))
      .finally(() => setInstanceLoading(false));
  }, [currentNodeType?.id]);

  // Reset selection when node changes
  useEffect(() => {
    setSelectedInstanceId(null);
    setAlsoSaveAsInstance(false);
  }, [node?.id]);

  if (!node) {
    // Panel state: generating
    if (aiLoading) {
      return (
        <div
          style={{
            width: 320,
            display: 'flex',
            flexDirection: 'column',
            alignItems: 'center',
            justifyContent: 'center',
            borderLeft: '1px solid #f0f0f0',
            background: '#fff',
            gap: 16,
            padding: 32,
          }}
        >
          <Spin size="large" />
          <Title level={5} style={{ margin: 0, color: '#595959' }}>
            AI is generating workflow…
          </Title>
          <Text type="secondary" style={{ textAlign: 'center', fontSize: 13 }}>
            Please wait, the workflow will be updated on the canvas
            automatically.
          </Text>
          <Button onClick={() => onAbort?.()}>Cancel</Button>
        </div>
      );
    }

    return (
      <div
        style={{
          width: 320,
          display: 'flex',
          flexDirection: 'column',
          borderLeft: '1px solid #f0f0f0',
          background: '#fff',
          overflow: 'hidden',
        }}
      >
        {/* Node palette */}
        <div style={{ flex: 1, overflow: 'auto', padding: 16 }}>
          <Text
            type="secondary"
            style={{ fontSize: 13, display: 'block', marginBottom: 12 }}
          >
            拖拽节点到左侧画布
          </Text>
          {Object.entries(
            nodeTypes.reduce<Record<string, WorkflowNodeType[]>>((acc, nt) => {
              const cat = nt.category || 'ACTION';
              if (!acc[cat]) acc[cat] = [];
              acc[cat].push(nt);
              return acc;
            }, {}),
          ).map(([category, types]) => {
            const colors = CATEGORY_COLORS[category] || CATEGORY_COLORS.ACTION;
            return (
              <div key={category} style={{ marginBottom: 12 }}>
                <Text
                  strong
                  style={{
                    fontSize: 12,
                    color: '#595959',
                    display: 'block',
                    marginBottom: 6,
                  }}
                >
                  {CATEGORY_LABELS[category] || category}
                </Text>
                <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
                  {types.map((nt) => (
                    <Tooltip
                      key={nt.type}
                      title={
                        <div
                          style={{
                            whiteSpace: 'pre-line',
                            fontSize: 12,
                            lineHeight: 1.6,
                          }}
                        >
                          {NODE_TOOLTIPS[nt.type] || nt.description || nt.label}
                        </div>
                      }
                      placement="left"
                      mouseEnterDelay={0.3}
                      overlayStyle={{ maxWidth: 360 }}
                    >
                      <div
                        draggable
                        onDragStart={(e) => {
                          e.dataTransfer.setData(
                            'application/workflow-node-type',
                            JSON.stringify(nt),
                          );
                          e.dataTransfer.effectAllowed = 'move';
                        }}
                        style={{
                          cursor: 'grab',
                          padding: '5px 10px',
                          background: colors.bg,
                          border: `1px solid ${colors.border}`,
                          borderRadius: 6,
                          fontSize: 12,
                          userSelect: 'none',
                        }}
                      >
                        {nt.label}
                      </div>
                    </Tooltip>
                  ))}
                </div>
              </div>
            );
          })}
        </div>
      </div>
    );
  }

  function handleConfigChange(key: string, value: any) {
    setConfig((prev) => ({ ...prev, [key]: value }));
    setDirty(true);
  }

  function handleLabelChange(value: string) {
    setLabel(value);
    if (node) {
      onLabelChange(node.id, value);
    }
  }

  async function handleSave() {
    if (!node) return;
    // Clean undefined values
    const cleanConfig: Record<string, any> = {};
    for (const [k, v] of Object.entries(config)) {
      if (v !== undefined && v !== '') cleanConfig[k] = v;
    }

    if (alsoSaveAsInstance && currentNodeType && newInstanceName.trim()) {
      // Save node AND create a new instance, then reference it
      setSaveAsLoading(true);
      try {
        const res = await customRequest<any>(
          'workflowNodeInstance/createOne',
          'POST',
          {
            nodeTypeId: Number(currentNodeType.id),
            name: newInstanceName.trim(),
            description: newInstanceDesc.trim() || null,
            config: cleanConfig,
            isEnabled: true,
          },
        );
        const newId = String(res.id);
        onSave(node.id, {
          label,
          config: {},
          instanceRef: {
            instanceId: newId,
            instanceName: newInstanceName.trim(),
          },
        });
        setInstances((prev) => [
          ...prev,
          {
            id: newId,
            name: newInstanceName.trim(),
            description: newInstanceDesc.trim() || null,
            config: cleanConfig,
          },
        ]);
        setAlsoSaveAsInstance(false);
        setNewInstanceName('');
        setNewInstanceDesc('');
        message.success('已保存并创建新实例');
      } catch (e: any) {
        message.error(e?.message ?? '创建实例失败');
      } finally {
        setSaveAsLoading(false);
      }
    } else {
      onSave(node.id, { label, config: cleanConfig });
      message.success('节点已保存');
    }
    setDirty(false);
  }

  function handleAiSubmit() {
    if (!node || !prompt.trim()) return;
    onAiEdit(node.id, prompt.trim());
    setPrompt('');
  }

  function handleReferenceInstance() {
    if (!node || !selectedInstanceId) return;
    const inst = instances.find((i) => i.id === selectedInstanceId);
    if (!inst) return;
    onSave(node.id, {
      label: inst.name,
      config: {},
      instanceRef: { instanceId: inst.id, instanceName: inst.name },
    });
    setLabel(inst.name);
    setSelectedInstanceId(null);
  }

  function handleCopyAsTemplate() {
    if (!node || !selectedInstanceId) return;
    const inst = instances.find((i) => i.id === selectedInstanceId);
    if (!inst) return;
    setConfig(inst.config);
    setLabel(inst.name);
    setDirty(true);
    setSelectedInstanceId(null);
    if (node) {
      onLabelChange(node.id, inst.name);
    }
    message.success('已加载实例配置，可自由修改');
  }

  function handleDetachReference() {
    if (!node) return;
    const inst = instances.find((i) => i.id === node.instanceRef?.instanceId);
    const resolvedConfig = inst?.config ?? {};
    onSave(node.id, { config: resolvedConfig, instanceRef: null });
  }

  // Render a config field based on its schema
  function renderConfigField(key: string, schema: any) {
    const isRequired = requiredFields.includes(key);
    const value = config[key];
    const placeholder = FIELD_PLACEHOLDERS[key] || '';

    // Special: workflowId → Select with workflow list
    if (key === 'workflowId') {
      return (
        <div key={key}>
          <Text
            type="secondary"
            style={{ fontSize: 13, display: 'block', marginBottom: 2 }}
          >
            {isRequired && <span style={{ color: '#ff4d4f' }}>* </span>}
            {key}
          </Text>
          <Select
            value={value || undefined}
            onChange={(v) => handleConfigChange(key, v)}
            placeholder="选择子流程"
            options={workflowOptions}
            style={{ width: '100%' }}
            allowClear
            showSearch
            filterOption={(input, option) =>
              (option?.label ?? '').toLowerCase().includes(input.toLowerCase())
            }
          />
        </div>
      );
    }

    // String with enum → Select
    if (schema.type === 'string' && schema.enum) {
      return (
        <div key={key}>
          <Text
            type="secondary"
            style={{ fontSize: 13, display: 'block', marginBottom: 2 }}
          >
            {isRequired && <span style={{ color: '#ff4d4f' }}>* </span>}
            {key}
          </Text>
          <Select
            value={value || undefined}
            onChange={(v) => handleConfigChange(key, v)}
            placeholder={`选择 ${key}`}
            options={schema.enum.map((e: string) => ({ label: e, value: e }))}
            style={{ width: '100%' }}
          />
        </div>
      );
    }

    // String → Input or TextArea
    if (schema.type === 'string') {
      const useTextArea = TEXTAREA_FIELDS.has(key);
      return (
        <div key={key}>
          <Text
            type="secondary"
            style={{ fontSize: 13, display: 'block', marginBottom: 2 }}
          >
            {isRequired && <span style={{ color: '#ff4d4f' }}>* </span>}
            {key}
          </Text>
          {useTextArea ? (
            <TextArea
              value={value ?? ''}
              onChange={(e) => handleConfigChange(key, e.target.value)}
              placeholder={placeholder}
              autoSize={{ minRows: 2, maxRows: 6 }}
            />
          ) : (
            <Input
              value={value ?? ''}
              onChange={(e) => handleConfigChange(key, e.target.value)}
              placeholder={placeholder}
            />
          )}
        </div>
      );
    }

    // Number → InputNumber
    if (schema.type === 'number') {
      return (
        <div key={key}>
          <Text
            type="secondary"
            style={{ fontSize: 13, display: 'block', marginBottom: 2 }}
          >
            {isRequired && <span style={{ color: '#ff4d4f' }}>* </span>}
            {key}
          </Text>
          <InputNumber
            value={value}
            onChange={(v) => handleConfigChange(key, v)}
            placeholder={placeholder}
            style={{ width: '100%' }}
            min={0}
          />
        </div>
      );
    }

    // Boolean → Switch
    if (schema.type === 'boolean') {
      return (
        <div
          key={key}
          style={{
            display: 'flex',
            justifyContent: 'space-between',
            alignItems: 'center',
          }}
        >
          <Text type="secondary" style={{ fontSize: 13 }}>
            {isRequired && <span style={{ color: '#ff4d4f' }}>* </span>}
            {key}
          </Text>
          <Switch
            checked={!!value}
            onChange={(checked) => handleConfigChange(key, checked)}
            size="small"
          />
        </div>
      );
    }

    // Array of strings → Select mode="tags"
    if (schema.type === 'array' && schema.items?.type === 'string') {
      return (
        <div key={key}>
          <Text
            type="secondary"
            style={{ fontSize: 13, display: 'block', marginBottom: 2 }}
          >
            {isRequired && <span style={{ color: '#ff4d4f' }}>* </span>}
            {key}
          </Text>
          <Select
            mode="tags"
            value={Array.isArray(value) ? value : []}
            onChange={(v) => handleConfigChange(key, v)}
            placeholder={`输入后按 Enter 添加`}
            style={{ width: '100%' }}
            tokenSeparators={[',']}
          />
        </div>
      );
    }

    // Array of objects (e.g. switch cases) → dynamic list
    if (schema.type === 'array' && schema.items?.type === 'object') {
      const items: any[] = Array.isArray(value) ? value : [];
      const itemProps = schema.items.properties || {};
      return (
        <div key={key}>
          <Text
            type="secondary"
            style={{ fontSize: 13, display: 'block', marginBottom: 4 }}
          >
            {isRequired && <span style={{ color: '#ff4d4f' }}>* </span>}
            {key}
          </Text>
          {items.map((item, idx) => (
            <div
              key={idx}
              style={{
                display: 'flex',
                gap: 4,
                marginBottom: 4,
                alignItems: 'center',
              }}
            >
              {Object.keys(itemProps).map((propKey) => (
                <Input
                  key={propKey}
                  value={item[propKey] ?? ''}
                  onChange={(e) => {
                    const newItems = [...items];
                    newItems[idx] = {
                      ...newItems[idx],
                      [propKey]: e.target.value,
                    };
                    handleConfigChange(key, newItems);
                  }}
                  placeholder={propKey}
                  size="small"
                  style={{ flex: 1 }}
                />
              ))}
              <MinusCircleOutlined
                style={{ color: '#ff4d4f', cursor: 'pointer', fontSize: 14 }}
                onClick={() => {
                  const newItems = items.filter((_, i) => i !== idx);
                  handleConfigChange(key, newItems);
                }}
              />
            </div>
          ))}
          <Button
            type="dashed"
            size="small"
            icon={<PlusOutlined />}
            onClick={() => {
              const emptyItem: Record<string, string> = {};
              for (const propKey of Object.keys(itemProps)) {
                emptyItem[propKey] = '';
              }
              handleConfigChange(key, [...items, emptyItem]);
            }}
            block
          >
            添加
          </Button>
        </div>
      );
    }

    // Object → JSON TextArea
    if (schema.type === 'object') {
      const displayValue =
        typeof value === 'object'
          ? JSON.stringify(value, null, 2)
          : (value ?? '');
      return (
        <div key={key}>
          <Text
            type="secondary"
            style={{ fontSize: 13, display: 'block', marginBottom: 2 }}
          >
            {isRequired && <span style={{ color: '#ff4d4f' }}>* </span>}
            {key}
            <Text type="secondary" style={{ fontSize: 11 }}>
              {' '}
              (JSON)
            </Text>
          </Text>
          <TextArea
            value={displayValue === '{}' ? '' : displayValue}
            onChange={(e) => {
              const raw = e.target.value;
              try {
                handleConfigChange(key, JSON.parse(raw));
              } catch {
                // Keep as string if not valid JSON yet (user is typing)
                handleConfigChange(key, raw);
              }
            }}
            placeholder={`{"key": "value"}`}
            autoSize={{ minRows: 2, maxRows: 5 }}
          />
        </div>
      );
    }

    // Fallback: plain input
    return (
      <div key={key}>
        <Text
          type="secondary"
          style={{ fontSize: 13, display: 'block', marginBottom: 2 }}
        >
          {isRequired && <span style={{ color: '#ff4d4f' }}>* </span>}
          {key}
        </Text>
        <Input
          value={
            typeof value === 'string' ? value : JSON.stringify(value ?? '')
          }
          onChange={(e) => handleConfigChange(key, e.target.value)}
          placeholder={placeholder}
        />
      </div>
    );
  }

  return (
    <div
      style={{
        width: 320,
        borderLeft: '1px solid #f0f0f0',
        background: '#fff',
        display: 'flex',
        flexDirection: 'column',
        overflow: 'hidden',
      }}
    >
      {/* Header */}
      <div
        style={{
          padding: '16px 16px 12px',
          borderBottom: '1px solid #f0f0f0',
          display: 'flex',
          justifyContent: 'space-between',
          alignItems: 'flex-start',
        }}
      >
        <Space direction="vertical" size={4}>
          <Title level={5} style={{ margin: 0, fontSize: 14 }}>
            节点属性
          </Title>
          <Tag color="blue">{node.type}</Tag>
        </Space>
        <Button
          type="text"
          danger
          icon={<DeleteOutlined />}
          onClick={() => onDelete?.(node.id)}
          title="删除节点"
        />
      </div>

      {/* Tab Switcher */}
      <div
        style={{ padding: '12px 16px 0', borderBottom: '1px solid #f0f0f0' }}
      >
        <Segmented
          value={activeTab}
          onChange={(val) => setActiveTab(val as 'config' | 'ai')}
          options={[
            { label: '节点配置', value: 'config' },
            { label: 'AI 生成', value: 'ai' },
          ]}
          block
          style={{ marginBottom: 12 }}
        />
      </div>

      {/* Form */}
      <div style={{ flex: 1, overflow: 'auto', padding: 16 }}>
        {activeTab === 'config' && (
          <Space direction="vertical" size={12} style={{ width: '100%' }}>
            {/* Instance Selector / Reference Banner */}
            {!node.instanceRef && (
              <div
                style={{
                  padding: 12,
                  background: '#fafafa',
                  borderRadius: 6,
                }}
              >
                <Text
                  type="secondary"
                  style={{ fontSize: 12, display: 'block', marginBottom: 6 }}
                >
                  复用已有实例
                </Text>
                <Select
                  value={selectedInstanceId ?? undefined}
                  onChange={(v) => setSelectedInstanceId(v)}
                  placeholder={
                    instanceLoading
                      ? '加载中...'
                      : instances.length === 0
                        ? '暂无可用实例'
                        : '选择实例...'
                  }
                  options={instances.map((i) => ({
                    label: i.name,
                    value: i.id,
                  }))}
                  style={{ width: '100%', marginBottom: 8 }}
                  allowClear
                  showSearch
                  loading={instanceLoading}
                  disabled={instances.length === 0 && !instanceLoading}
                  filterOption={(input, option) =>
                    (option?.label ?? '')
                      .toLowerCase()
                      .includes(input.toLowerCase())
                  }
                />
                {selectedInstanceId && (
                  <Space size={8}>
                    <Button
                      size="small"
                      icon={<LinkOutlined />}
                      onClick={handleReferenceInstance}
                    >
                      引用（动态）
                    </Button>
                    <Button size="small" onClick={handleCopyAsTemplate}>
                      复制为模板
                    </Button>
                  </Space>
                )}
              </div>
            )}
            {node.instanceRef && (
              <div
                style={{
                  padding: '10px 12px',
                  background: '#e6f7ff',
                  borderRadius: 6,
                  display: 'flex',
                  alignItems: 'center',
                  justifyContent: 'space-between',
                }}
              >
                <Text style={{ fontSize: 13 }}>
                  🔗 引用: {node.instanceRef.instanceName}
                </Text>
                <Button
                  size="small"
                  icon={<DisconnectOutlined />}
                  onClick={handleDetachReference}
                >
                  解除引用
                </Button>
              </div>
            )}

            {/* Label */}
            <div>
              <Text
                strong
                style={{ fontSize: 14, display: 'block', marginBottom: 4 }}
              >
                Label
              </Text>
              <Input
                value={label}
                onChange={(e) => handleLabelChange(e.target.value)}
              />
            </div>

            {/* Schema-driven config fields */}
            {Object.keys(properties).length > 0 && (
              <div>
                <Text
                  strong
                  style={{ fontSize: 14, display: 'block', marginBottom: 8 }}
                >
                  Config
                </Text>
                {node.instanceRef ? (
                  <Space
                    direction="vertical"
                    size={8}
                    style={{ width: '100%' }}
                  >
                    {(() => {
                      const inst = instances.find(
                        (i) => i.id === node.instanceRef?.instanceId,
                      );
                      const refConfig = inst?.config ?? {};
                      return Object.entries(properties).map(([key]) => (
                        <div key={key}>
                          <Text
                            type="secondary"
                            style={{
                              fontSize: 13,
                              display: 'block',
                              marginBottom: 2,
                            }}
                          >
                            {key}
                          </Text>
                          <Text style={{ fontSize: 13, color: '#595959' }}>
                            {refConfig[key] !== undefined &&
                            refConfig[key] !== ''
                              ? typeof refConfig[key] === 'object'
                                ? JSON.stringify(refConfig[key])
                                : String(refConfig[key])
                              : '—'}
                          </Text>
                        </div>
                      ));
                    })()}
                  </Space>
                ) : (
                  <Space
                    direction="vertical"
                    size={10}
                    style={{ width: '100%' }}
                  >
                    {Object.entries(properties).map(([key, propSchema]) =>
                      renderConfigField(key, propSchema),
                    )}
                  </Space>
                )}
              </div>
            )}

            {/* Save section */}
            {!node.instanceRef && (
              <div>
                <Checkbox
                  checked={alsoSaveAsInstance}
                  onChange={(e) => setAlsoSaveAsInstance(e.target.checked)}
                  style={{ marginBottom: 8 }}
                >
                  <Text style={{ fontSize: 13 }}>同时保存为实例(模版)</Text>
                </Checkbox>
                {alsoSaveAsInstance && (
                  <Space
                    direction="vertical"
                    size={6}
                    style={{ width: '100%', marginBottom: 8 }}
                  >
                    <Input
                      value={newInstanceName}
                      onChange={(e) => setNewInstanceName(e.target.value)}
                      placeholder="实例名称（必填）"
                      size="small"
                    />
                    <Input
                      value={newInstanceDesc}
                      onChange={(e) => setNewInstanceDesc(e.target.value)}
                      placeholder="描述（可选）"
                      size="small"
                    />
                  </Space>
                )}
                <Button
                  type="primary"
                  block
                  loading={saveAsLoading}
                  disabled={alsoSaveAsInstance && !newInstanceName.trim()}
                  onClick={handleSave}
                >
                  保存修改
                </Button>
              </div>
            )}
          </Space>
        )}

        {activeTab === 'ai' && (
          <div>
            <Text
              type="secondary"
              style={{ fontSize: 13, display: 'block', marginBottom: 8 }}
            >
              描述复杂修改，AI 将更新此节点
            </Text>
            <div style={{ display: 'flex', gap: 4 }}>
              <TextArea
                value={prompt}
                onChange={(e) => setPrompt(e.target.value)}
                placeholder="例: 将超时改为30分钟..."
                autoSize={{ minRows: 4, maxRows: 8 }}
                onPressEnter={(e) => {
                  if (e.ctrlKey) handleAiSubmit();
                }}
              />
            </div>
            <Button
              type="default"
              icon={<SendOutlined />}
              loading={aiLoading}
              onClick={handleAiSubmit}
              disabled={!prompt.trim()}
              style={{ marginTop: 8 }}
              block
            >
              发送
            </Button>
          </div>
        )}
      </div>
    </div>
  );
}
