/**
 * PromptEditor — Agent 提示词编辑器
 *
 * @prop {'with-panel'|'editor-only'} [layout='with-panel'] — 是否展示右侧配置面板
 * @prop {'default'|'compact'} [density='default'] — 内容密度
 * @prop {'prompt'|'prompt-memory'} [inputMode='prompt'] — 模型输入模式
 * @prop {boolean} [skillMode=false] — 技能模式开关
 * @prop {string} [nodeDescription='节点描述占位节点描述占位'] — 节点说明
 * @prop {string} [modelName='Doubao 模型'] — 当前模型
 * @prop {Array} [sections] — Prompt 段落配置
 * @prop {Array} [variables] — 可插入变量 chip
 * @prop {Array} [actions] — 动作引用 tag
 * @prop {Array} [panelSections] — 右侧配置分区
 * @prop {'none'|'skills'|'node-config'|'single-mode'|'skill-params'} [expandedPanel] — 兼容旧版展开配置，当前仅影响单轮模式示例
 * @prop {'none'|'skills'|'node-config'|'single-mode'|'skill-params'} [defaultExpandedPanel='none'] — 兼容旧版默认展开配置
 * @prop {Object} [panelConfig] — 配置区内容数据
 * @prop {boolean} [editable=true] — Prompt 正文是否可编辑
 * @prop {boolean} [showCount=true] — 是否显示右下角字数统计
 * @prop {number} [maxCount=2000] — 字数上限
 * @prop {number} [count] — 受控字数
 * @prop {string} [placeholder='请输入 Prompt 正文'] — Prompt 正文空态提示
 * @prop {boolean} [showCoreInfoPanel=true] — 是否显示右侧顶部核心信息面板
 * @prop {boolean} [showVariableTags=false] — 是否以变量 tag 形式展示 Prompt 内变量
 * @prop {boolean} [showVariableInsert=false] — 是否显示底部 @ 变量唤起
 * @prop {boolean} [showActionInsert=false] — 是否显示底部模版唤起
 */

import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import Form from './Form';
import FormTitle from './FormTitle';
import Icon from './Icon';
import InputNumber from './InputNumber';
import Switch from './Switch';

const DEFAULT_VARIABLES = [
  '用户近10轮回复',
  '用户问题摘要',
  '当前处理阶段',
  '是否需要补充信息',
  '是否命中标准流程',
  '是否需要转人工',
  '推荐回复模板',
  '服务处理建议',
  '关联知识条目',
  '流程超时状态',
];

const DEFAULT_ACTIONS = [
  '按钮：查看处理进度 → 进度详情页',
  '按钮：补充问题信息 → 信息补充页',
];

const DEFAULT_SECTIONS = [
  {
    title: '# 角色',
    body: [
      '你是一名专业的抖音客服助手，能够理解用户诉求，并以亲切、清晰、得体的方式提供标准化回复。',
    ],
  },
  {
    title: '## 技能',
    body: [
      '针对通用客服咨询场景，综合用户对话上下文、变量信息和参考模板，给出简洁、准确且口径一致的回复。',
      { type: 'inline-variable', prefix: '用户对话上下文：', variable: '用户近10轮回复' },
    ],
  },
  {
    title: '## 用户信息',
    body: [
      '# 用户特征信息',
      { type: 'field', label: '用户问题摘要', variable: '用户问题摘要' },
      { type: 'field', label: '当前处理阶段', variable: '当前处理阶段' },
      { type: 'field', label: '是否需要补充信息', variable: '是否需要补充信息' },
      { type: 'field', label: '是否命中标准流程', variable: '是否命中标准流程' },
      { type: 'field', label: '是否需要转人工', variable: '是否需要转人工' },
      { type: 'field', label: '推荐回复模板', variable: '推荐回复模板' },
      { type: 'field', label: '服务处理建议', variable: '服务处理建议' },
      { type: 'field', label: '关联知识条目', variable: '关联知识条目' },
      { type: 'field', label: '流程超时状态', variable: '流程超时状态' },
    ],
  },
  {
    title: '## 回复流程',
    body: [
      '- 如果用户问题已经命中标准流程，优先使用推荐模板进行回复，并在句尾加上',
      "'''",
      { type: 'action', action: '按钮：查看处理进度 → 进度详情页' },
      "'''",
      '- 如果用户信息不完整，先说明需要补充的信息，并保持语气友好清晰',
      '- 如果需要用户继续操作，引导用户补充信息，并在句尾加上这段话',
      "'''",
      { type: 'action', action: '按钮：补充问题信息 → 信息补充页' },
      "'''",
      '- 如果当前问题无法自动处理，说明已记录关键信息，并建议转人工继续跟进',
      '    - 如果仍需等待处理结果，告知用户可以稍后查看进度',
      '    - 如果已有明确处理建议，根据上下文直接给出下一步建议',
      '- 如果用户情绪较强，先表达理解，再给出清晰的处理路径',
      '    - 回复需要简短、有同理心，避免堆叠规则或枚举无关情况',
    ],
  },
  {
    title: '## 输出格式',
    body: ['【问题】', 'xxxx', '【解决方案】', 'xxxx'],
  },
  {
    title: '## 注意',
    body: [
      '-【动作】后面的内容是特殊的跳转逻辑，需要**严格保持格式**',
      '- 你需要仔细阅读用户的特征信息，根据用户信息执行回复流程，给出合适的回复话术，不要做情况的枚举，直接回复。',
      '- 适当在句子中插入一个表情，增加对话的亲和力，但是不要每一句话都加，要根据上下文和用户情绪适当使用。',
      '- 敏锐识别用户情绪，尤其关注抱怨、吐槽、谩骂等负面情绪，然后根据用户的情绪进行安抚，要有同理心。',
      '- 单句表达要简短，要说大白话，针对用户的一次提问，不要给出长篇大论的回复，要简单明了。',
    ],
  },
];

const DEFAULT_PANEL_SECTIONS = [
  { id: 'type-1', label: '类型 1 配置' },
  { id: 'type-2', label: '类型 2 配置' },
  { id: 'type-3', label: '类型 3 配置' },
];

const DEFAULT_PANEL_CONFIG = {
  skills: {
    emptyText: '暂无技能 & 知识引用',
  },
  nodeConfig: {
    fallback: [
      { id: 'global-policy', label: '通用策略', checked: true },
      { id: 'safe-check', label: '安全校验', checked: true },
      { id: 'template-match', label: '模板匹配', checked: true },
    ],
    advanced: [
      { id: 'single-mode', label: '单轮模式', checked: false },
      { id: 'dynamic-template', label: '动态模板', checked: false },
    ],
    maxLoopCount: 2,
    singleMode: {
      selected: 'reply',
      options: [
        { value: 'reply', label: '生成客服回复' },
        { value: 'assign', label: '将结果赋值给变量' },
      ],
    },
  },
  skillParams: {
    selected: ['user_query', 'context_summary'],
    fields: [
      { name: 'user_query', desc: '用户问题输入', placeholder: '请输入参数说明' },
      { name: 'context_summary', desc: '上下文摘要', placeholder: '请输入参数说明' },
    ],
  },
};

const ROOT = 'tfds-prompt-editor flex h-full min-h-[520px] w-full overflow-hidden rounded-lg border border-border-default bg-white text-foreground [font-family:inherit]';
const LAYOUT_CLASS = {
  'with-panel': '',
  'editor-only': '[&_[data-prompt-panel]]:hidden',
};
const DENSITY_CLASS = {
  default: {
    editor: 'gap-3 px-5 py-4',
    body: 'leading-6',
    panel: 'px-5 py-4',
  },
  compact: {
    editor: 'gap-2 px-4 py-3',
    body: 'leading-5',
    panel: 'px-4 py-3',
  },
};
const EDITOR = 'flex min-w-0 flex-1 flex-col';
const SCROLLER = 'min-h-0 flex-1 overflow-y-auto rounded-lg bg-white';
const EDITOR_BODY = 'flex min-h-0 flex-1 flex-col';
const CONTENT = [
  'relative flex min-w-0 flex-1 flex-col items-start text-sm text-foreground',
  'w-full whitespace-pre-wrap break-words [overflow-wrap:anywhere]',
  '[&>div]:w-full [&>div]:whitespace-pre-wrap [&>div]:break-words [&>div]:text-sm [&>div]:leading-6 [&>div]:text-foreground',
  '[&>p]:w-full [&>p]:whitespace-pre-wrap [&>p]:break-words [&_br]:block',
  'data-[empty=true]:before:pointer-events-none data-[empty=true]:before:text-foreground-disabled data-[empty=true]:before:content-[attr(data-placeholder)]',
  'rounded-md focus-visible:outline-none',
].join(' ');
const COUNT = 'shrink-0 px-1 text-sm leading-5 text-foreground-muted';
const SECTION = 'flex w-full min-w-0 flex-col gap-1';
const LINE = 'm-0 min-w-0 whitespace-pre-wrap break-words text-sm text-foreground [overflow-wrap:anywhere]';
const RICH_TEXT_BLOCK = {
  h1: 'm-0 min-w-0 whitespace-pre-wrap break-words text-base leading-6 text-foreground [font-weight:var(--font-semibold)] [overflow-wrap:anywhere]',
  h2: 'm-0 min-w-0 whitespace-pre-wrap break-words text-sm leading-6 text-foreground [font-weight:var(--font-semibold)] [overflow-wrap:anywhere]',
  h3: 'm-0 min-w-0 whitespace-pre-wrap break-words text-sm leading-5 text-foreground [font-weight:var(--font-semibold)] [overflow-wrap:anywhere]',
  paragraph: LINE,
  list: 'm-0 flex min-w-0 items-start gap-2 text-sm leading-6 text-foreground [overflow-wrap:anywhere]',
  quote: 'm-0 min-w-0 rounded-md border-l-2 border-brand-500 bg-blueGrey-50 px-2 py-1 text-sm leading-6 text-foreground [overflow-wrap:anywhere]',
  code: 'rounded bg-fill px-1 py-0.5 text-[13px] leading-5 text-foreground',
};
const FIELD_LINE = 'flex min-w-0 flex-wrap items-start gap-x-2 gap-y-1';
const VARIABLE_CHIP = 'inline-flex min-h-6 max-w-full items-center gap-1.5 rounded-md bg-blue-50 px-2 py-0.5 text-sm leading-5 text-foreground';
const TOOLBAR = 'flex h-10 shrink-0 items-center justify-between gap-2';
const TOOLBAR_GROUP = 'flex min-w-0 flex-1 items-center gap-2';
const TOOL_BUTTON = [
  'inline-flex size-9 items-center justify-center rounded-lg border border-border-default bg-white text-foreground',
  'transition-colors duration-150 hover:bg-fill focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blueGrey-400',
].join(' ');
const PANEL = 'flex h-full w-[380px] shrink-0 flex-col gap-4 border-l border-border-default bg-white';
const PANEL_CARD = 'flex w-full flex-col gap-3 rounded-xl border border-blueGrey-200 p-4';
const PANEL_ROW = 'flex min-w-0 items-center gap-3';
const PANEL_LABEL = 'w-[60px] shrink-0 text-sm leading-5 text-foreground-muted';
const NODE_DESC = 'flex min-w-0 items-center gap-2 rounded-md py-1 text-sm leading-5 text-foreground';
const MODEL_ROW = 'flex min-w-0 items-center gap-2 rounded-lg px-0.5 py-1';
const MODEL_MARK = 'text-brand-500';
const RADIO_GROUP = 'flex items-start gap-2';
const INPUT_MODE_CLASS = {
  prompt: {
    prompt: 'border-brand-500 text-link',
    'prompt-memory': 'border-border-default text-foreground-muted',
  },
  'prompt-memory': {
    prompt: 'border-border-default text-foreground-muted',
    'prompt-memory': 'border-brand-500 text-link',
  },
};
const RADIO_BASE = 'inline-flex h-8 shrink-0 items-center rounded-lg border bg-white px-3 text-xs [font-weight:var(--font-semibold)] leading-4';
const PANEL_SCROLL = 'flex w-full min-h-0 flex-1 flex-col overflow-y-auto';
const CONFIG_SECTION = 'flex w-full flex-col gap-3';
const CONFIG_DIVIDER = 'my-6 h-0 w-full shrink-0 border-t border-dashed border-border-default/70';
const ADD_BUTTON = 'inline-flex h-8 items-center gap-2 rounded-md px-2 text-sm [font-weight:var(--font-semibold)] leading-5 text-link transition-colors duration-150 hover:bg-brand-50 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-200';
const EMPTY_PANEL = 'flex w-full items-center justify-center py-4 text-sm leading-5 text-foreground-disabled';
const PANEL_CONTENT = 'flex w-full flex-col gap-3';
const PANEL_SUBTITLE = 'py-0.5 text-xs leading-4 text-foreground-muted';
const CONFIG_CARD = 'flex min-h-9 w-full flex-col gap-2 rounded-md border border-border-default bg-blueGrey-50 px-3 py-1.5';
const CONFIG_ROW = 'flex w-full min-w-0 items-center justify-between gap-3';
const CONFIG_LABEL = 'min-w-0 flex-1 whitespace-nowrap text-sm leading-5 text-foreground';
const CONFIG_SPACER = 'h-2 w-full';
const NUMBER_FIELD = [
  '!w-[68px] !min-w-[68px] !max-w-[68px] !shrink-0 !grow-0 !basis-[68px] !flex-none',
  '[&_.tfds-input-number]:!w-[68px] [&_.tfds-input-number]:!min-w-[68px] [&_.tfds-input-number]:!max-w-[68px] [&_.tfds-input-number]:!flex-none',
  '[&_.tfds-input-number]:h-7 [&_.tfds-input-number]:!bg-blueGrey-50 [&_.tfds-input-number]:px-3 [&_.tfds-input-number]:pr-2',
  '[&_.tfds-input-number_input]:!bg-transparent',
  '[&_.tfds-input-number]:text-sm [&_.tfds-input-number]:leading-5',
].join(' ');
const TAG_SELECT = 'flex min-h-8 w-full items-center justify-between rounded-md bg-fill px-1 py-1';
const PARAM_TAG = 'inline-flex h-6 items-center gap-1.5 rounded border border-border-default bg-white px-2 text-xs leading-4 text-foreground';
const PARAM_FIELD = 'flex w-full flex-col gap-1';
const PARAM_LABEL = 'flex h-9 w-full min-w-0 flex-col justify-center';
const PARAM_NAME = 'text-sm leading-5 text-foreground';
const PARAM_DESC = 'truncate text-xs leading-4 text-foreground-muted';
const PARAM_INPUT = 'flex h-9 w-full items-center rounded-md bg-fill px-3 text-sm leading-5 text-foreground-muted';

function PromptVariableChip({ children }) {
  return (
    <span className={VARIABLE_CHIP}>
      <Icon name="brackets-check-stroked" size={14} className="text-blue-500" />
      <span className="min-w-0 break-words">{children}</span>
    </span>
  );
}

function renderInlineRichText(text) {
  const parts = String(text).split(/(\*\*[^*]+\*\*|`[^`]+`)/g).filter(Boolean);

  return parts.map((part, index) => {
    if (part.startsWith('**') && part.endsWith('**')) {
      return (
        <strong key={`${part}-${index}`} className="[font-weight:var(--font-semibold)]">
          {part.slice(2, -2)}
        </strong>
      );
    }

    if (part.startsWith('`') && part.endsWith('`')) {
      return (
        <code key={`${part}-${index}`} className={RICH_TEXT_BLOCK.code}>
          {part.slice(1, -1)}
        </code>
      );
    }

    return <span key={`${part}-${index}`}>{part}</span>;
  });
}

function getPromptText(node, fallback = '') {
  const text = node?.innerText ?? fallback;
  const normalized = text.replace(/\u00A0/g, ' ').replace(/\u200B/g, '');
  return normalized.trim().length > 0 ? normalized : '';
}

function insertEditableLineBreak() {
  if (typeof window === 'undefined' || typeof document === 'undefined') return false;

  const selection = window.getSelection?.();
  if (!selection || selection.rangeCount === 0) return false;

  const range = selection.getRangeAt(0);
  range.deleteContents();

  const lineBreak = document.createTextNode('\n');
  range.insertNode(lineBreak);
  range.setStartAfter(lineBreak);
  range.collapse(true);

  selection.removeAllRanges();
  selection.addRange(range);
  return true;
}

function RichTextLine({ text }) {
  const value = String(text);
  const heading = value.match(/^(#{1,6})\s+(.+)$/);
  if (heading) {
    const level = Math.min(heading[1].length, 3);
    const Tag = `h${level}`;
    return <Tag className={RICH_TEXT_BLOCK[`h${level}`]}>{renderInlineRichText(heading[2])}</Tag>;
  }

  if (value.startsWith('- ')) {
    return (
      <p className={RICH_TEXT_BLOCK.list}>
        <span className="mt-[9px] size-1 shrink-0 rounded-full bg-brand-500" aria-hidden="true" />
        <span className="min-w-0 flex-1 whitespace-pre-wrap break-words">{renderInlineRichText(value.slice(2))}</span>
      </p>
    );
  }

  if (value.startsWith('> ')) {
    return <p className={RICH_TEXT_BLOCK.quote}>{renderInlineRichText(value.slice(2))}</p>;
  }

  return <p className={RICH_TEXT_BLOCK.paragraph}>{renderInlineRichText(value)}</p>;
}

function PromptVariableValue({ showVariableTags, children }) {
  if (showVariableTags) {
    return <PromptVariableChip>{children}</PromptVariableChip>;
  }

  return <span className="min-w-0 break-words text-foreground">{children}</span>;
}

function PromptLine({ item, showVariableTags }) {
  if (typeof item === 'string') {
    return <RichTextLine text={item} />;
  }

  if (item?.type === 'inline-variable') {
    return (
      <p className={FIELD_LINE}>
        <span>{item.prefix}</span>
        <PromptVariableValue showVariableTags={showVariableTags}>{item.variable}</PromptVariableValue>
      </p>
    );
  }

  if (item?.type === 'field') {
    return (
      <p className={FIELD_LINE}>
        <span className="text-brand-500">-</span>
        <span>{`${item.label}：`}</span>
        <PromptVariableValue showVariableTags={showVariableTags}>{item.variable}</PromptVariableValue>
      </p>
    );
  }

  if (item?.type === 'action') {
    return <PromptVariableValue showVariableTags={showVariableTags}>{item.action}</PromptVariableValue>;
  }

  return null;
}

function InputModeOption({ active, children, onClick }) {
  return (
    <button type="button" className={[RADIO_BASE, active].join(' ')} onClick={onClick}>
      {children}
    </button>
  );
}

function normalizePanelSections(panelSections) {
  return panelSections.map((section) => {
    if (typeof section === 'string') {
      const found = DEFAULT_PANEL_SECTIONS.find((item) => item.label === section);
      return found || { id: section, label: section };
    }
    return section;
  });
}

function AddButton({ children = '添加', onClick }) {
  return (
    <button type="button" className={ADD_BUTTON} onClick={onClick}>
      <Icon name="plus-stroked" size="sm" />
      <span>{children}</span>
    </button>
  );
}

function ConfigSwitchRow({ label, checked }) {
  return (
    <div className={CONFIG_CARD}>
      <div className={CONFIG_ROW}>
        <span className={CONFIG_LABEL}>{label}</span>
        <Switch size="sm" defaultChecked={checked} />
      </div>
    </div>
  );
}

function ConfigSwitchWithFields({ label, checked = true, children }) {
  const [open, setOpen] = useState(checked);

  return (
    <div className={CONFIG_CARD}>
      <div className={CONFIG_ROW}>
        <span className={CONFIG_LABEL}>{label}</span>
        <Switch size="sm" checked={open} onChange={setOpen} />
      </div>
      {open ? <div className="flex w-full flex-col gap-3 pt-1">{children}</div> : null}
    </div>
  );
}

function ConfigNumberField({ label, defaultValue = 2, min = 1, max = 99 }) {
  return (
    <div className={CONFIG_CARD}>
      <div className={CONFIG_ROW}>
        <span className={CONFIG_LABEL}>{label}</span>
        <InputNumber
          className={NUMBER_FIELD}
          innerButtons
          min={min}
          max={max}
          step={1}
          defaultValue={defaultValue}
          aria-label={label}
        />
      </div>
    </div>
  );
}

function PanelOption({ selected, children }) {
  return (
    <span className={[RADIO_BASE, selected ? 'border-brand-500 text-link' : 'border-border-default text-foreground-muted'].join(' ')}>
      {children}
    </span>
  );
}

function SkillsPanel({ config, onAdd }) {
  return (
    <div className={PANEL_CONTENT}>
      <div className={EMPTY_PANEL}>{config?.emptyText || '暂无技能 & 知识引用'}</div>
    </div>
  );
}

function NodeConfigPanel({ config, mode = 'default' }) {
  const fallback = config?.fallback || DEFAULT_PANEL_CONFIG.nodeConfig.fallback;
  const advanced = config?.advanced || DEFAULT_PANEL_CONFIG.nodeConfig.advanced;
  const singleMode = config?.singleMode || DEFAULT_PANEL_CONFIG.nodeConfig.singleMode;

  return (
    <div className={PANEL_CONTENT}>
      <div className={PANEL_SUBTITLE}>兜底配置</div>
      {fallback.map((item) => (
        <ConfigSwitchRow key={item.id} label={item.label} checked={item.checked} />
      ))}

      <div className={CONFIG_SPACER} />
      <div className={PANEL_SUBTITLE}>高级配置</div>
      {advanced.map((item) => {
        const showSingleOptions = mode === 'single-mode' && item.id === 'single-mode';
        return (
          <div key={item.id} className={CONFIG_CARD}>
            <div className={CONFIG_ROW}>
              <span className={CONFIG_LABEL}>{item.label}</span>
              <Switch size="sm" defaultChecked={showSingleOptions || item.checked} />
            </div>
            {showSingleOptions ? (
              <div className="flex flex-wrap gap-2">
                {singleMode.options.map((option) => (
                  <PanelOption key={option.value} selected={option.value === singleMode.selected}>
                    {option.label}
                  </PanelOption>
                ))}
              </div>
            ) : null}
          </div>
        );
      })}

      <ConfigNumberField label="最大循环次数" defaultValue={config?.maxLoopCount ?? 2} />
    </div>
  );
}

function SkillParamsPanel({ config }) {
  const selected = config?.selected || DEFAULT_PANEL_CONFIG.skillParams.selected;
  const fields = config?.fields || DEFAULT_PANEL_CONFIG.skillParams.fields;

  return (
    <div className={PANEL_CONTENT}>
      <div className={TAG_SELECT}>
        <div className="flex min-w-0 flex-wrap gap-1">
          {selected.map((tag) => (
            <span key={tag} className={PARAM_TAG}>
              {tag}
              <span className="text-foreground-muted">×</span>
            </span>
          ))}
        </div>
        <Icon name="chevron-down-stroked" size="sm" className="text-foreground-secondary" />
      </div>

      {fields.map((field) => (
        <div key={field.name} className={PARAM_FIELD}>
          <div className={PARAM_LABEL}>
            <span className={PARAM_NAME}>{field.name}</span>
            <span className={PARAM_DESC}>{field.desc}</span>
          </div>
          <div className={PARAM_INPUT}>{field.placeholder}</div>
        </div>
      ))}
    </div>
  );
}

function GenericConfigPanel({ section }) {
  if (section.id === 'type-2') {
    return (
      <div className={PANEL_CONTENT}>
        <Form
          className="!gap-3"
          items={[{
            id: 'type-2-strategy',
            label: '执行策略',
            type: 'select',
            defaultValue: 'balanced',
            options: [
              { value: 'default', label: '默认策略' },
              { value: 'balanced', label: '平衡策略' },
              { value: 'strict', label: '严格策略' },
            ],
          }]}
        />
        <Form
          className="!gap-3"
          items={[{
            id: 'type-2-fallback',
            label: '备用回复模板',
            type: 'input',
            defaultValue: '当前问题已记录，请等待后续处理',
            allowClear: true,
          }]}
        />
        <ConfigSwitchRow label="动态模板" checked={false} />
      </div>
    );
  }

  if (section.id === 'type-3') {
    return (
      <div className={PANEL_CONTENT}>
        <Form
          className="!gap-3"
          items={[
            {
              id: 'type-3-output',
              label: '输出变量',
              type: 'input',
              defaultValue: 'agent_reply',
              allowClear: true,
            },
            {
              id: 'type-3-result-type',
              label: '结果类型',
              type: 'select',
              defaultValue: 'text',
              options: [
                { value: 'text', label: '文本回复' },
                { value: 'action', label: '动作结果' },
                { value: 'json', label: '结构化 JSON' },
              ],
            },
          ]}
        />
        <ConfigSwitchRow label="日志追踪" checked />
      </div>
    );
  }

  return (
    <div className={PANEL_CONTENT}>
      <ConfigSwitchWithFields label="启用配置" checked>
        <Form
          className="!gap-3"
          items={[
            {
              id: 'type-1-trigger',
              label: '模板类型',
              type: 'select',
              defaultValue: 'standard',
              options: [
                { value: 'standard', label: '标准模板' },
                { value: 'guide', label: '引导模板' },
                { value: 'fallback', label: '兜底模板' },
              ],
            },
            {
              id: 'type-1-name',
              label: '配置名称',
              type: 'input',
              defaultValue: '默认模板配置',
              allowClear: true,
            },
          ]}
        />
        <ConfigNumberField label="最大循环次数" defaultValue={2} />
      </ConfigSwitchWithFields>
    </div>
  );
}

function ConfigSection({
  section,
  panelConfig,
  mode,
  onAddPanelItem,
}) {
  const isSkills = section.id === 'skills';
  const isNodeConfig = section.id === 'node-config';
  const isSkillParams = section.id === 'skill-params';
  const isKnownSection = isSkills || isNodeConfig || isSkillParams;

  return (
    <div className={CONFIG_SECTION}>
      <FormTitle
        variant="level-3"
        title={section.label}
        className="py-0.5 [&>div]:gap-x-[10px]"
        titleSuffix={isSkills ? <AddButton onClick={() => onAddPanelItem?.(section.id)} /> : null}
      />
      {isSkills ? <SkillsPanel config={panelConfig?.skills} /> : null}
      {isNodeConfig ? <NodeConfigPanel config={panelConfig?.nodeConfig} mode={mode} /> : null}
      {isSkillParams ? <SkillParamsPanel config={panelConfig?.skillParams} /> : null}
      {!isKnownSection ? <GenericConfigPanel section={section} /> : null}
    </div>
  );
}

export default function PromptEditor({
  layout = 'with-panel',
  density = 'default',
  inputMode = 'prompt',
  skillMode = false,
  nodeDescription = '节点描述占位节点描述占位',
  modelName = 'Doubao 模型',
  sections = DEFAULT_SECTIONS,
  variables = DEFAULT_VARIABLES,
  actions = DEFAULT_ACTIONS,
  panelSections = DEFAULT_PANEL_SECTIONS,
  expandedPanel,
  defaultExpandedPanel = 'none',
  panelConfig = DEFAULT_PANEL_CONFIG,
  editable = true,
  showCount = true,
  maxCount = 2000,
  count,
  placeholder = '请输入 Prompt 正文',
  showCoreInfoPanel = true,
  showVariableTags = false,
  showVariableInsert = false,
  showActionInsert = false,
  onPromptInput,
  onSkillModeChange,
  onInputModeChange,
  onInsertVariable,
  onInsertAction,
  onAddPanelItem,
  className = '',
  ...rest
}) {
  const densityClass = DENSITY_CLASS[density] || DENSITY_CLASS.default;
  const normalizedPanelSections = normalizePanelSections(panelSections);
  const contentRef = useRef(null);
  const [innerCount, setInnerCount] = useState(0);
  const [isPromptEmpty, setIsPromptEmpty] = useState(false);
  const [innerSkillMode, setInnerSkillMode] = useState(skillMode);
  const [innerInputMode, setInnerInputMode] = useState(inputMode);
  const inputModeClass = INPUT_MODE_CLASS[innerInputMode] || INPUT_MODE_CLASS.prompt;
  const panelMode = expandedPanel ?? defaultExpandedPanel;

  const fallbackCountSeed = useMemo(() => (
    sections
      .flatMap((section) => [section.title, ...(section.body || [])])
      .map((item) => {
        if (typeof item === 'string') return item;
        if (item?.type === 'inline-variable') return `${item.prefix || ''}${item.variable || ''}`;
        if (item?.type === 'field') return `${item.label || ''}${item.variable || ''}`;
        if (item?.type === 'action') return item.action || '';
        return '';
      })
      .join('\n')
  ), [sections]);

  useEffect(() => {
    const text = getPromptText(contentRef.current, fallbackCountSeed);
    setInnerCount(text.length);
    setIsPromptEmpty(text.length === 0);
  }, [fallbackCountSeed]);

  useEffect(() => {
    setInnerSkillMode(skillMode);
  }, [skillMode]);

  useEffect(() => {
    setInnerInputMode(inputMode);
  }, [inputMode]);

  const handlePromptInput = useCallback((event) => {
    const text = getPromptText(event.currentTarget);
    if (text.length === 0) {
      event.currentTarget.innerHTML = '';
    }
    setInnerCount(text.length);
    setIsPromptEmpty(text.length === 0);
    onPromptInput?.(event);
  }, [onPromptInput]);

  const handlePromptKeyDown = useCallback((event) => {
    if (!editable || event.key !== 'Enter' || event.nativeEvent?.isComposing) return;

    event.preventDefault();
    if (!insertEditableLineBreak()) return;

    const inputEvent = typeof InputEvent === 'function'
      ? new InputEvent('input', { bubbles: true, inputType: 'insertLineBreak' })
      : new Event('input', { bubbles: true });
    event.currentTarget.dispatchEvent(inputEvent);

    window.requestAnimationFrame(() => {
      if (contentRef.current) {
        const text = getPromptText(contentRef.current);
        setInnerCount(text.length);
        setIsPromptEmpty(text.length === 0);
      }
    });
  }, [editable]);

  const handleSkillModeChange = useCallback((next, event) => {
    setInnerSkillMode(next);
    onSkillModeChange?.(next, event);
  }, [onSkillModeChange]);

  const handleInputModeChange = useCallback((next) => {
    setInnerInputMode(next);
    onInputModeChange?.(next);
  }, [onInputModeChange]);

  return (
    <section
      className={[ROOT, LAYOUT_CLASS[layout], className].filter(Boolean).join(' ')}
      data-tfds-component="PromptEditor"
      {...rest}
    >
      <div className={[EDITOR, densityClass.editor].join(' ')}>
        <div className={EDITOR_BODY}>
          <div className={SCROLLER}>
          <div
            ref={contentRef}
            className={[CONTENT, densityClass.body].join(' ')}
            contentEditable={editable}
            suppressContentEditableWarning
            role={editable ? 'textbox' : undefined}
            aria-multiline={editable || undefined}
            aria-placeholder={editable ? placeholder : undefined}
            tabIndex={editable ? 0 : undefined}
            data-placeholder={placeholder}
            data-empty={isPromptEmpty ? 'true' : undefined}
            onInput={handlePromptInput}
            onKeyDown={handlePromptKeyDown}
          >
            {sections.map((section) => (
              <section key={section.title} className={SECTION}>
                <RichTextLine text={section.title} />
                {section.body.map((item, index) => (
                  <PromptLine key={`${section.title}-${index}`} item={item} showVariableTags={showVariableTags} />
                ))}
                <p className={LINE} aria-hidden="true">&nbsp;</p>
              </section>
            ))}
          </div>
          </div>
        </div>

        <div className={TOOLBAR}>
          <div className={TOOLBAR_GROUP}>
            {showVariableInsert ? (
              <button type="button" className={TOOL_BUTTON} aria-label="插入变量" onClick={() => onInsertVariable?.(variables[0])}>
                <Icon name="at-sign-stroked" size="sm" />
              </button>
            ) : null}
            {showActionInsert ? (
              <button type="button" className={TOOL_BUTTON} aria-label="模版唤起" onClick={() => onInsertAction?.(actions[0])}>
                <Icon name="puzzle-piece-02-stroked" size="sm" />
              </button>
            ) : null}
          </div>
          {showCount ? (
            <div className={COUNT} aria-label={`当前字数 ${count ?? innerCount}，上限 ${maxCount}`}>
              {count ?? innerCount} / {maxCount}
            </div>
          ) : null}
        </div>
      </div>

      <aside className={[PANEL, densityClass.panel].join(' ')} data-prompt-panel>
        {showCoreInfoPanel ? (
          <div className={PANEL_CARD}>
            <div className={PANEL_ROW}>
              <span className={PANEL_LABEL}>技能模式</span>
              <Switch size="sm" checked={innerSkillMode} onChange={handleSkillModeChange} />
            </div>

            <div className={PANEL_ROW}>
              <span className={PANEL_LABEL}>节点信息</span>
              <span className={NODE_DESC}>
                <span className="min-w-0 truncate">{nodeDescription}</span>
                <Icon name="edit-03-stroked" size={14} className="text-foreground-secondary" />
              </span>
            </div>

            <div className={PANEL_ROW}>
              <span className={PANEL_LABEL}>模型选型</span>
              <span className={MODEL_ROW}>
                <Icon name="hiai-logo" size="sm" className={MODEL_MARK} aria-hidden="true" />
                <span className="whitespace-nowrap text-sm leading-5 text-foreground">{modelName}</span>
                <Icon name="settings-04-stroked" size="sm" className="text-foreground-secondary" />
              </span>
            </div>

            <div className={PANEL_ROW}>
              <span className={PANEL_LABEL}>模型输入</span>
              <div className={RADIO_GROUP} role="radiogroup" aria-label="模型输入">
                <InputModeOption active={inputModeClass.prompt} onClick={() => handleInputModeChange('prompt')}>
                  仅 Prompt
                </InputModeOption>
                <InputModeOption active={inputModeClass['prompt-memory']} onClick={() => handleInputModeChange('prompt-memory')}>
                  Prompt + 记忆
                </InputModeOption>
              </div>
            </div>
          </div>
        ) : null}

        <div className={PANEL_SCROLL}>
          {normalizedPanelSections.map((section, index) => (
            <div key={section.id} className="w-full">
              {index > 0 ? <div className={CONFIG_DIVIDER} aria-hidden="true" /> : null}
              <ConfigSection
                section={section}
                panelConfig={panelConfig}
                mode={panelMode}
                onAddPanelItem={onAddPanelItem}
              />
            </div>
          ))}
        </div>
      </aside>
    </section>
  );
}
