import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
  Alert,
  Button,
  Card,
  Input,
  Select,
  Space,
  Tag,
  Tree,
  Typography,
} from "antd";
import type { DataNode, TreeProps } from "antd/es/tree";
import {
  PlusOutlined,
  DeleteOutlined,
  BranchesOutlined,
} from "@ant-design/icons";
import { useTagConfig } from "@/hooks/useTagConfig";
import {
  getControlDisplayName,
  getObjectDisplayName,
  type LabelStudioTagConfig,
} from "../annotation.tagconfig";
import { DataType } from "../annotation.model";

const { Text, Title } = Typography;

interface XmlNode {
  id: string;
  tag: string;
  attrs: Record<string, string>;
  children: XmlNode[];
  text?: string;
}

interface ValidationIssue {
  errors: string[];
  warnings: string[];
}

interface TemplateConfigurationTreeEditorProps {
  value?: string;
  onChange?: (xml: string) => void;
  readOnly?: boolean;
  readOnlyStructure?: boolean;
  height?: number | string;
  dataType?: DataType;
}

const DEFAULT_ROOT_TAG = "View";
const CHILD_TAGS = ["Label", "Choice", "Relation", "Item", "Path", "Channel"];
const OBJECT_TAGS_BY_DATA_TYPE: Record<DataType, string[]> = {
  [DataType.TEXT]: ["Text", "Paragraphs", "Markdown"],
  [DataType.IMAGE]: ["Image", "Bitmask"],
  [DataType.AUDIO]: ["Audio", "AudioPlus"],
  [DataType.VIDEO]: ["Video"],
  [DataType.PDF]: ["PDF"],
  [DataType.TIMESERIES]: ["Timeseries", "TimeSeries", "Vector"],
  [DataType.CHAT]: ["Chat"],
  [DataType.HTML]: ["HyperText", "Markdown"],
  [DataType.TABLE]: ["Table", "Vector"],
};

const createId = () =>
  `node_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;

const createEmptyTree = (): XmlNode => ({
  id: createId(),
  tag: DEFAULT_ROOT_TAG,
  attrs: {},
  children: [],
});

const escapeAttributeValue = (value: string) =>
  value
    .replace(/&/g, "&amp;")
    .replace(/"/g, "&quot;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;");

const escapeTextValue = (value: string) =>
  value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");

const parseXmlToTree = (xml: string): { tree: XmlNode | null; error?: string } => {
  try {
    const parser = new DOMParser();
    const doc = parser.parseFromString(xml, "text/xml");
    const parserError = doc.getElementsByTagName("parsererror");
    if (parserError.length > 0) {
      return { tree: null, error: "XML 格式错误，请检查标签闭合与层级。" };
    }

    const root = doc.documentElement;
    if (!root || root.tagName !== DEFAULT_ROOT_TAG) {
      return { tree: null, error: "根节点必须是 <View>。" };
    }

    const buildNode = (element: Element): XmlNode => {
      const attrs: Record<string, string> = {};
      Array.from(element.attributes).forEach((attr) => {
        attrs[attr.name] = attr.value;
      });

      const elementChildren = Array.from(element.childNodes).filter(
        (node) => node.nodeType === Node.ELEMENT_NODE
      ) as Element[];

      const textNodes = Array.from(element.childNodes).filter(
        (node) => node.nodeType === Node.TEXT_NODE && node.textContent?.trim()
      );

      const children = elementChildren.map(buildNode);
      const text =
        children.length === 0
          ? textNodes.map((node) => node.textContent ?? "").join("").trim()
          : undefined;

      return {
        id: createId(),
        tag: element.tagName,
        attrs,
        children,
        text: text || undefined,
      };
    };

    return { tree: buildNode(root) };
  } catch (error) {
    console.error("解析 XML 失败", error);
    return { tree: null, error: "解析 XML 失败，请检查配置内容。" };
  }
};

const serializeTreeToXml = (node: XmlNode, indent = 0): string => {
  const pad = "  ".repeat(indent);
  const attrs = Object.entries(node.attrs)
    .filter(([key]) => key.trim())
    .map(([key, value]) => ` ${key}="${escapeAttributeValue(value)}"`)
    .join("");

  if (node.children.length === 0 && !node.text) {
    return `${pad}<${node.tag}${attrs} />`;
  }

  const lines: string[] = [`${pad}<${node.tag}${attrs}>`];

  if (node.text) {
    lines.push(`${pad}  ${escapeTextValue(node.text)}`);
  }

  node.children.forEach((child) => {
    lines.push(serializeTreeToXml(child, indent + 1));
  });

  lines.push(`${pad}</${node.tag}>`);
  return lines.join("\n");
};

const updateNode = (
  node: XmlNode,
  targetId: string,
  updater: (current: XmlNode) => XmlNode
): XmlNode => {
  if (node.id === targetId) {
    return updater(node);
  }
  let changed = false;
  const children = node.children.map((child) => {
    const updated = updateNode(child, targetId, updater);
    if (updated !== child) changed = true;
    return updated;
  });
  return changed ? { ...node, children } : node;
};

const removeNodeById = (
  node: XmlNode,
  targetId: string
): { node: XmlNode; removed?: XmlNode } => {
  const index = node.children.findIndex((child) => child.id === targetId);
  if (index >= 0) {
    const removed = node.children[index];
    const children = node.children.filter((child) => child.id !== targetId);
    return { node: { ...node, children }, removed };
  }

  let removed: XmlNode | undefined;
  const children = node.children.map((child) => {
    const result = removeNodeById(child, targetId);
    if (result.removed) removed = result.removed;
    return result.node;
  });
  if (removed) {
    return { node: { ...node, children }, removed };
  }
  return { node };
};

const findNodeWithParent = (
  node: XmlNode,
  targetId: string,
  parent?: XmlNode
): { node?: XmlNode; parent?: XmlNode; index?: number } => {
  if (node.id === targetId) {
    return { node, parent, index: parent?.children.findIndex((c) => c.id === targetId) };
  }
  for (const child of node.children) {
    const result = findNodeWithParent(child, targetId, node);
    if (result.node) return result;
  }
  return {};
};

const isDescendant = (node: XmlNode, targetId: string): boolean => {
  if (node.id === targetId) return true;
  return node.children.some((child) => isDescendant(child, targetId));
};

const COMMON_TAG_DISPLAY_NAMES: Record<string, string> = {
  View: "容器",
  Header: "标题",
  Style: "样式",
  Label: "标签项",
  Choice: "选项",
  Relation: "关系",
  Relations: "关系组",
  Item: "列表项",
  Path: "路径",
  Channel: "通道",
  Collapse: "折叠面板",
  Filter: "过滤器",
  Shortcut: "快捷键",
};

const getDefaultName = (tag: string) => {
  const lower = tag.toLowerCase();
  if (lower.includes("text")) return "text";
  if (lower.includes("image")) return "image";
  if (lower.includes("audio")) return "audio";
  if (lower.includes("video")) return "video";
  if (lower.includes("pdf")) return "pdf";
  if (lower.includes("chat")) return "chat";
  if (lower.includes("table")) return "table";
  if (lower.includes("timeseries") || lower.includes("time")) return "ts";
  return lower;
};

const createNode = (
  tag: string,
  config: LabelStudioTagConfig | null,
  objectNames: string[]
): XmlNode => {
  const attrs: Record<string, string> = {};
  const controlConfig = config?.controls?.[tag];
  const objectConfig = config?.objects?.[tag];
  const requiredAttrs = controlConfig?.required_attrs || objectConfig?.required_attrs || [];

  requiredAttrs.forEach((attr) => {
    attrs[attr] = "";
  });

  if (objectConfig) {
    const name = getDefaultName(tag);
    if (!attrs.name) {
      attrs.name = name;
    }
    if (!attrs.value) {
      attrs.value = `$${attrs.name}`;
    }
  }

  if (controlConfig) {
    const isLabeling = controlConfig.category === "labeling";

    if (isLabeling) {
      if (!attrs.name) {
        attrs.name = getDefaultName(tag);
      }
      if (!attrs.toName) {
        attrs.toName = objectNames[0] || "";
      }
    } else {
      // For layout controls, only fill if required
      if (attrs.name !== undefined && !attrs.name) {
        attrs.name = getDefaultName(tag);
      }
      if (attrs.toName !== undefined && !attrs.toName) {
        attrs.toName = objectNames[0] || "";
      }
    }
  }

  if (CHILD_TAGS.includes(tag)) {
    attrs.value = attrs.value || "";
  }

  const node: XmlNode = {
    id: createId(),
    tag,
    attrs,
    children: [],
  };

  if (controlConfig?.requires_children && controlConfig.child_tag) {
    node.children.push({
      id: createId(),
      tag: controlConfig.child_tag,
      attrs: { value: "" },
      children: [],
    });
  }

  if (tag === "Style") {
    node.text = "";
  }

  return node;
};

const collectObjectNames = (node: XmlNode, config: LabelStudioTagConfig | null): string[] => {
  const names: string[] = [];
  const objectTags = new Set(Object.keys(config?.objects || {}));
  const traverse = (current: XmlNode) => {
    if (objectTags.has(current.tag) && current.attrs.name) {
      names.push(current.attrs.name);
    }
    current.children.forEach(traverse);
  };
  traverse(node);
  return names;
};

const validateTree = (
  node: XmlNode,
  config: LabelStudioTagConfig | null
): Record<string, ValidationIssue> => {
  const issues: Record<string, ValidationIssue> = {};
  const objectTags = new Set(Object.keys(config?.objects || {}));
  const controlTags = new Set(Object.keys(config?.controls || {}));
  const objectNames = new Set(collectObjectNames(node, config));

  const ensureIssue = (id: string) => {
    if (!issues[id]) {
      issues[id] = { errors: [], warnings: [] };
    }
    return issues[id];
  };

  let labelingControlCount = 0;
  let objectCount = 0;

  const validateNode = (current: XmlNode) => {
    const issue = ensureIssue(current.id);
    const controlConfig = config?.controls?.[current.tag];
    const objectConfig = config?.objects?.[current.tag];
    const isObject = objectTags.has(current.tag);
    const isControl = controlTags.has(current.tag);
    const isLabelingControl = isControl && controlConfig?.category === "labeling";

    if (isObject) {
      objectCount += 1;
      const requiredAttrs = objectConfig?.required_attrs || [];
      requiredAttrs.forEach((attr) => {
        if (!current.attrs[attr]) {
          issue.errors.push(`对象缺少必填属性: ${attr}`);
        }
      });
      if (current.attrs.value && !current.attrs.value.startsWith("$")) {
        issue.errors.push("对象 value 应以 $ 开头");
      }
    }

    if (isControl) {
      if (isLabelingControl) {
        labelingControlCount += 1;
        const requiredAttrs = controlConfig?.required_attrs || [];
        requiredAttrs.forEach((attr) => {
          if (!current.attrs[attr]) {
            issue.errors.push(`控件缺少必填属性: ${attr}`);
          }
        });
        if (current.attrs.toName) {
          const toNames = current.attrs.toName
            .split(",")
            .map((name) => name.trim())
            .filter(Boolean);
          const invalid = toNames.filter((name) => !objectNames.has(name));
          if (invalid.length > 0) {
            issue.errors.push(`toName 未找到对象: ${invalid.join(", ")}`);
          }
        }
      } else {
        const requiredAttrs = controlConfig?.required_attrs || [];
        requiredAttrs.forEach((attr) => {
          if (!current.attrs[attr]) {
            issue.warnings.push(`布局标签缺少建议属性: ${attr}`);
          }
        });
      }

      if (controlConfig?.requires_children && controlConfig.child_tag) {
        const children = current.children.filter(
          (child) => child.tag === controlConfig.child_tag
        );
        if (children.length === 0) {
          const message = `${current.tag} 需要至少一个 <${controlConfig.child_tag}> 子节点`;
          if (isLabelingControl) {
            issue.errors.push(message);
          } else {
            issue.warnings.push(message);
          }
        }
        children.forEach((child) => {
          if (!child.attrs.value) {
            const message = `<${child.tag}> 缺少 value 属性`;
            const childIssue = ensureIssue(child.id);
            if (isLabelingControl) {
              childIssue.errors.push(message);
            } else {
              childIssue.warnings.push(message);
            }
          }
        });
      }
    }

    current.children.forEach(validateNode);
  };

  validateNode(node);

  if (node.tag === DEFAULT_ROOT_TAG) {
    const rootIssue = ensureIssue(node.id);
    if (objectCount === 0) {
      rootIssue.errors.push("至少需要一个数据对象标签");
    }
    if (labelingControlCount === 0) {
      rootIssue.errors.push("至少需要一个标注控件标签");
    }
  }

  return issues;
};

const TemplateConfigurationTreeEditor = ({
  value,
  onChange,
  readOnly = false,
  readOnlyStructure = false,
  height = 420,
  dataType,
}: TemplateConfigurationTreeEditorProps) => {
  const { config } = useTagConfig(false);
  const [tree, setTree] = useState<XmlNode>(() => createEmptyTree());
  const [selectedId, setSelectedId] = useState<string>(tree.id);
  const [parseError, setParseError] = useState<string | null>(null);
  const lastSerialized = useRef<string>("");

  useEffect(() => {
    if (!value) {
      const empty = createEmptyTree();
      setTree(empty);
      setSelectedId(empty.id);
      setParseError(null);
      lastSerialized.current = "";
      return;
    }

    if (value === lastSerialized.current) return;
    const result = parseXmlToTree(value);
    if (result.tree) {
      setTree(result.tree);
      setSelectedId(result.tree.id);
      setParseError(null);
      lastSerialized.current = value;
    } else if (result.error) {
      setParseError(result.error);
    }
  }, [value]);

  useEffect(() => {
    const xml = serializeTreeToXml(tree);
    lastSerialized.current = xml;
    onChange?.(xml);
  }, [tree, onChange]);

  const objectNames = useMemo(
    () => collectObjectNames(tree, config || null),
    [tree, config]
  );
  const validationIssues = useMemo(
    () => validateTree(tree, config || null),
    [tree, config]
  );

  const selectedNode = useMemo(() => {
    const result = findNodeWithParent(tree, selectedId);
    return result.node || tree;
  }, [tree, selectedId]);

  const selectedIssue = validationIssues[selectedNode.id] || {
    errors: [],
    warnings: [],
  };

  const isStructureLocked = readOnly || readOnlyStructure;
  const canEditAttributeValues = !readOnly && selectedNode.id !== tree.id;
  const canEditAttributeKeys = canEditAttributeValues && !readOnlyStructure;
  const canEditTagName = canEditAttributeKeys;
  const canEditText = canEditAttributeValues && !readOnlyStructure;

  const controlOptions = useMemo(() => {
    if (!config?.controls) return { labeling: [], layout: [] };
    const labeling = Object.entries(config.controls)
      .filter(([, item]) => item.category === "labeling")
      .map(([tag]) => ({ value: tag, label: getControlDisplayName(tag) }));
    const layout = Object.entries(config.controls)
      .filter(([, item]) => item.category === "layout")
      .map(([tag]) => ({
        value: tag,
        label: COMMON_TAG_DISPLAY_NAMES[tag] || tag,
      }));
    return { labeling, layout };
  }, [config]);

  const objectOptions = useMemo(() => {
    if (!config?.objects) return [];
    const options = Object.keys(config.objects).map((tag) => ({
      value: tag,
      label: getObjectDisplayName(tag),
    }));
    if (!dataType) return options;
    const allowedTags = OBJECT_TAGS_BY_DATA_TYPE[dataType];
    if (!allowedTags) return options;
    const allowedSet = new Set(allowedTags);
    const filtered = options.filter((option) => allowedSet.has(option.value));
    return filtered.length > 0 ? filtered : options;
  }, [config, dataType]);

  const tagOptions = useMemo(() => {
    const options = [] as {
      label: string;
      options: { value: string; label: string }[];
    }[];
    options.push({
      label: "容器",
      options: [{ value: "View", label: COMMON_TAG_DISPLAY_NAMES.View }],
    });
    if (objectOptions.length > 0) {
      options.push({ label: "数据对象", options: objectOptions });
    }
    if (controlOptions.labeling.length > 0) {
      options.push({ label: "标注控件", options: controlOptions.labeling });
    }
    if (controlOptions.layout.length > 0) {
      options.push({ label: "布局标签", options: controlOptions.layout });
    }
    options.push({
      label: "子标签",
      options: CHILD_TAGS.map((tag) => ({
        value: tag,
        label: COMMON_TAG_DISPLAY_NAMES[tag] || tag,
      })),
    });
    return options;
  }, [objectOptions, controlOptions]);

  const getTagDisplayName = useCallback(
    (tag: string) => {
      if (config?.objects?.[tag]) return getObjectDisplayName(tag);
      if (config?.controls?.[tag]) return getControlDisplayName(tag);
      return COMMON_TAG_DISPLAY_NAMES[tag] || tag;
    },
    [config]
  );

  const handleAddNode = (tag: string, mode: "child" | "sibling") => {
    if (isStructureLocked) return;
    const newNode = createNode(tag, config || null, objectNames);

    if (mode === "child") {
      setTree((prev) =>
        updateNode(prev, selectedNode.id, (current) => ({
          ...current,
          children: [...current.children, newNode],
        }))
      );
      setSelectedId(newNode.id);
      return;
    }

    const { parent } = findNodeWithParent(tree, selectedNode.id);
    if (!parent) return;
    setTree((prev) =>
      updateNode(prev, parent.id, (current) => {
        const index = current.children.findIndex(
          (child) => child.id === selectedNode.id
        );
        const children = [...current.children];
        children.splice(index + 1, 0, newNode);
        return { ...current, children };
      })
    );
    setSelectedId(newNode.id);
  };

  const handleDeleteNode = () => {
    if (isStructureLocked || selectedNode.id === tree.id) return;
    const { node: nextTree } = removeNodeById(tree, selectedNode.id);
    setTree(nextTree);
    setSelectedId(nextTree.id);
  };

  const handleAttrKeyChange = (oldKey: string, newKey: string) => {
    if (!canEditAttributeKeys) return;
    setTree((prev) =>
      updateNode(prev, selectedNode.id, (current) => {
        const attrs = { ...current.attrs };
        const value = attrs[oldKey] ?? "";
        delete attrs[oldKey];
        if (newKey) {
          attrs[newKey] = value;
        }
        return { ...current, attrs };
      })
    );
  };

  const handleAttrValueChange = (key: string, value: string) => {
    if (!canEditAttributeValues) return;
    setTree((prev) =>
      updateNode(prev, selectedNode.id, (current) => ({
        ...current,
        attrs: { ...current.attrs, [key]: value },
      }))
    );
  };

  const handleAddAttribute = () => {
    if (!canEditAttributeKeys) return;
    setTree((prev) =>
      updateNode(prev, selectedNode.id, (current) => {
        const attrs = { ...current.attrs };
        let index = 1;
        let key = "attr";
        while (attrs[key]) {
          index += 1;
          key = `attr${index}`;
        }
        attrs[key] = "";
        return { ...current, attrs };
      })
    );
  };

  const handleRemoveAttribute = (key: string) => {
    if (!canEditAttributeKeys) return;
    setTree((prev) =>
      updateNode(prev, selectedNode.id, (current) => {
        const attrs = { ...current.attrs };
        delete attrs[key];
        return { ...current, attrs };
      })
    );
  };

  const handleTagNameChange = (value: string) => {
    if (!canEditTagName) return;
    setTree((prev) =>
      updateNode(prev, selectedNode.id, (current) => ({
        ...current,
        tag: value || current.tag,
      }))
    );
  };

  const handleTextChange = (value: string) => {
    if (!canEditText) return;
    setTree((prev) =>
      updateNode(prev, selectedNode.id, (current) => ({
        ...current,
        text: value,
      }))
    );
  };

  const treeData: DataNode[] = useMemo(() => {
    const build = (node: XmlNode): DataNode => {
      const issue = validationIssues[node.id];
      const hasError = issue?.errors?.length > 0;
      const hasWarning = issue?.warnings?.length > 0;
      const name = node.attrs.name || node.attrs.value;
      const title = name
        ? `${getTagDisplayName(node.tag)} (${name})`
        : getTagDisplayName(node.tag);
      return {
        key: node.id,
        title: (
          <Space size={6}>
            <span className={hasError ? "text-red-600" : undefined}>
              {title}
            </span>
            {hasError && <Tag color="red">错误</Tag>}
            {!hasError && hasWarning && <Tag color="gold">提示</Tag>}
          </Space>
        ),
        children: node.children.map(build),
        draggable: !isStructureLocked && node.id !== tree.id,
      };
    };
    return [build(tree)];
  }, [tree, validationIssues, isStructureLocked, getTagDisplayName]);

  const onDrop: TreeProps["onDrop"] = (info) => {
    if (isStructureLocked) return;
    const dragId = String(info.dragNode.key);
    const dropId = String(info.node.key);

    if (dragId === tree.id) return;
    if (dragId === dropId) return;

    const dragInfo = findNodeWithParent(tree, dragId);
    const dropInfo = findNodeWithParent(tree, dropId);
    if (!dragInfo.node || !dropInfo.node) return;

    if (isDescendant(dragInfo.node, dropId)) {
      return;
    }

    const removedResult = removeNodeById(tree, dragId);
    let nextTree = removedResult.node;
    const movingNode = removedResult.removed;
    if (!movingNode) return;

    if (!info.dropToGap) {
      nextTree = updateNode(nextTree, dropId, (current) => ({
        ...current,
        children: [...current.children, movingNode],
      }));
    } else {
      const { parent } = findNodeWithParent(nextTree, dropId);
      if (!parent) return;
      nextTree = updateNode(nextTree, parent.id, (current) => {
        const index = current.children.findIndex((child) => child.id === dropId);
        const children = [...current.children];
        const insertIndex = info.dropPosition > 0 ? index + 1 : index;
        children.splice(insertIndex, 0, movingNode);
        return { ...current, children };
      });
    }

    setTree(nextTree);
    setSelectedId(movingNode.id);
  };

  return (
    <div className="grid grid-cols-2 gap-4" style={{ minHeight: 360 }}>
      <Card
        title={
          <Space>
            <BranchesOutlined />
            <span>结构树</span>
          </Space>
        }
        size="small"
        style={{ height }}
        bodyStyle={{ height: "100%", overflow: "auto" }}
      >
        {parseError && (
          <Alert message={parseError} type="error" showIcon style={{ marginBottom: 8 }} />
        )}
        <Tree
          key={tree.id}
          treeData={treeData}
          selectedKeys={[selectedId]}
          onSelect={(keys) => {
            if (keys.length > 0) setSelectedId(String(keys[0]));
          }}
          defaultExpandAll
          draggable={!isStructureLocked}
          onDrop={onDrop}
          blockNode
        />
      </Card>

      <Card title="节点配置" size="small" style={{ height }} bodyStyle={{ height: "100%", overflow: "auto" }}>
        <Space direction="vertical" size="middle" style={{ width: "100%" }}>
          <div>
            <Title level={5} style={{ margin: 0 }}>
              {selectedNode.tag}
            </Title>
            <Text type="secondary">{selectedNode.id}</Text>
          </div>

          <div className="grid grid-cols-2 gap-2">
            <Select
              placeholder="添加子节点"
              options={tagOptions}
              value={null}
              onChange={(value) => {
                handleAddNode(value, "child");
              }}
              disabled={isStructureLocked}
            />
            <Select
              placeholder="添加同级节点"
              options={tagOptions}
              value={null}
              onChange={(value) => {
                handleAddNode(value, "sibling");
              }}
              disabled={isStructureLocked || selectedNode.id === tree.id}
            />
            <Button
              danger
              icon={<DeleteOutlined />}
              onClick={handleDeleteNode}
              disabled={isStructureLocked || selectedNode.id === tree.id}
            >
              删除节点
            </Button>
          </div>

          <div>
            <Text>标签名称</Text>
            <Input
              value={selectedNode.tag}
              onChange={(e) => handleTagNameChange(e.target.value)}
              disabled={!canEditTagName}
            />
          </div>

          {selectedNode.tag === "Style" || selectedNode.text !== undefined ? (
            <div>
              <Text>文本内容</Text>
              <Input.TextArea
                value={selectedNode.text}
                onChange={(e) => handleTextChange(e.target.value)}
                rows={4}
                disabled={!canEditText}
              />
            </div>
          ) : null}

          <div>
            <Space direction="vertical" size="small" style={{ width: "100%" }}>
              <Space align="center" style={{ width: "100%", justifyContent: "space-between" }}>
                <Text>属性配置</Text>
                <Button size="small" onClick={handleAddAttribute} disabled={!canEditAttributeKeys} icon={<PlusOutlined />}>
                  添加属性
                </Button>
              </Space>
              {Object.entries(selectedNode.attrs).length === 0 && (
                <Text type="secondary">暂无属性</Text>
              )}
              {Object.entries(selectedNode.attrs).map(([key, value]) => (
                <Space key={key} align="baseline" style={{ width: "100%" }}>
                  <Input
                    value={key}
                    onChange={(e) => handleAttrKeyChange(key, e.target.value)}
                    placeholder="属性名"
                    style={{ width: 120 }}
                    disabled={!canEditAttributeKeys}
                  />
                  {key === "toName" ? (
                    <Select
                      mode="multiple"
                      style={{ flex: 1 }}
                      placeholder="选择对象"
                      value={value
                        .split(",")
                        .map((item) => item.trim())
                        .filter(Boolean)}
                      onChange={(values) => handleAttrValueChange(key, values.join(","))}
                      options={objectNames.map((name) => ({ value: name, label: name }))}
                      disabled={!canEditAttributeValues}
                    />
                  ) : (
                    <Input
                      value={value}
                      onChange={(e) => handleAttrValueChange(key, e.target.value)}
                      placeholder="属性值"
                      style={{ flex: 1 }}
                      disabled={!canEditAttributeValues}
                    />
                  )}
                  <Button
                    size="small"
                    danger
                    onClick={() => handleRemoveAttribute(key)}
                    disabled={!canEditAttributeKeys}
                  >
                    移除
                  </Button>
                </Space>
              ))}
            </Space>
          </div>

          {(selectedIssue.errors.length > 0 || selectedIssue.warnings.length > 0) && (
            <div>
              <Text strong>校验提示</Text>
              <div className="mt-2 space-y-1">
                {selectedIssue.errors.map((err, index) => (
                  <Tag key={`err-${index}`} color="red">
                    {err}
                  </Tag>
                ))}
                {selectedIssue.warnings.map((warn, index) => (
                  <Tag key={`warn-${index}`} color="gold">
                    {warn}
                  </Tag>
                ))}
              </div>
            </div>
          )}
        </Space>
      </Card>
    </div>
  );
};

export default TemplateConfigurationTreeEditor;
