/* ============================================================
   Beacon CS — TRAIN (skill authoring · six-segment editor ·
   Review suggestions (green add / red del / yellow mod) · redline checker)
   ============================================================ */
function faqDisplayId(faq) {
  if (!faq) return "";
  return [faq.logicalId || faq.id, faq.version].filter(Boolean).join(" · ");
}

function TrainView({ intent = {} }) {
  const { SKILLS, DOCUMENTS, FAQ_QUEUE, CONVERSATION_GAPS } = window.DATA;
  const { Badge } = window;
  const Icons = window.Icons;
  const [sel, setSel] = useState(null);
  const [tab, setTab] = useState("documents");
  const [doc, setDoc] = useState(null);
  const [documents, setDocuments] = useState(DOCUMENTS);
  const [faqs, setFaqs] = useState(FAQ_QUEUE);
  const [search, setSearch] = useState("");
  const [typeFilter, setTypeFilter] = useState("all");
  const [admissionFilter, setAdmissionFilter] = useState("all");
  const [languageFilter, setLanguageFilter] = useState("all");
  const [statusFilter, setStatusFilter] = useState("all");
  const [validityFilter, setValidityFilter] = useState("all");
  const [reviewPane, setReviewPane] = useState("faqs");
  const [sourceOpen, setSourceOpen] = useState(false);
  const [faqOpen, setFaqOpen] = useState(null);
  const [validityOpen, setValidityOpen] = useState(null);
  const [gapOpen, setGapOpen] = useState(null);
  const [relatedDoc, setRelatedDoc] = useState(null);
  useEffect(() => {
    const t = intent.tab || window.BeaconNav?.intent("train")?.tab;
    if (t) setTab(t);
  }, [intent.tab]);
  const openTarget = (section, nextTab) => window.BeaconNav?.go(section, nextTab ? { tab: nextTab } : {});
  const rejectFAQ = (id) => setFaqs(items => items.filter(f => f.id !== id));
  const mergeFAQ = (id) => setFaqs(items => items.filter(f => f.id !== id));
  const saveFAQ = (draft) => {
    setFaqs(items => {
      if (draft.id) {
        if (draft.status === "published") return items;
        return items.map(item => item.id === draft.id ? { ...item, ...draft } : item);
      }
      const nextId = Math.max(...items.map(item => Number((item.logicalId || item.id).replace(/\D/g, "")) || 0)) + 1;
      return [...items, {
        ...draft,
        id: `FAQ-${nextId}`,
        version: draft.version || "v1 待审核",
        liveVersion: draft.liveVersion || "无",
        liveStatus: draft.liveStatus || "尚未发布",
        draftStep: "编辑中",
        status: "pending",
      }];
    });
    setFaqOpen(null);
  };
  const createFAQVersion = (faq) => setFaqOpen({
    ...faq,
    id: "",
    logicalId: faq.logicalId || faq.id,
    version: "新版本待审核",
    liveVersion: faq.status === "published" ? faq.version : faq.liveVersion || faq.version,
    liveStatus: faq.status === "published" ? "已发布" : faq.liveStatus || "已停用",
    draftStep: "编辑中",
    status: "pending",
    createMode: "人工创建",
  });
  const activeDoc = documents.find(item => item.id === doc?.id) || documents[0];
  const visibleDocuments = documents.filter(item => {
    const query = search.trim().toLowerCase();
    const matchesSearch = !query || [item.title, item.id, item.scope].some(value => value.toLowerCase().includes(query));
    const matchesType = typeFilter === "all" || item.type === typeFilter;
    const matchesAdmission = admissionFilter === "all" || item.admission === admissionFilter;
    const matchesLanguage = languageFilter === "all" || item.language === languageFilter;
    const matchesStatus = statusFilter === "all" || item.status === statusFilter;
    const matchesValidity = validityFilter === "all"
      || (validityFilter === "active" && ["current", "synced", "needs-review"].includes(item.status))
      || (validityFilter === "attention" && ["needs-review", "expired", "retired"].includes(item.status));
    return matchesSearch && matchesType && matchesAdmission && matchesLanguage && matchesStatus && matchesValidity;
  });
  useEffect(() => {
    if (visibleDocuments.length && !visibleDocuments.some(item => item.id === activeDoc.id)) {
      setDoc(visibleDocuments[0]);
    }
  }, [search, typeFilter, admissionFilter, languageFilter, statusFilter, validityFilter, documents, activeDoc.id]);
  const selectDocument = (item) => {
    setDoc(item);
    if (window.innerWidth <= 720) {
      requestAnimationFrame(() => document.querySelector(".captain-side")?.scrollIntoView({ behavior: "smooth", block: "start" }));
    }
  };
  const updateDocumentStatus = (id, status) => {
    setDocuments(items => items.map(item => item.id === id
      ? {
          ...item,
          status,
          answerReason: status === "current"
            ? item.admission === "高风险政策"
              ? "当前版本已确认只用于人工维护问答或处理流程，不能直接放行高风险结论"
              : item.admission === "上线/交易对"
                ? "当前版本已确认转实时来源，不使用静态文章回答"
                : "当前版本已通过当前性、适用范围、风险和冲突检查"
            : status === "needs-review"
              ? "已进入人工复核；完成前不可用于回答"
              : status === "retired"
                ? "已人工停止使用并保留退出记录"
                : item.answerReason,
          admission: status === "current" && item.admission === "待判断" ? "核心客服" : item.admission,
          route: status === "current"
            ? item.admission === "高风险政策"
              ? item.route
              : item.admission === "上线/交易对"
                ? "仅实时来源"
                : "低风险引用"
            : status === "needs-review"
              ? "复核完成前不可用于回答"
              : status === "retired"
                ? "不用于回答"
                : item.route,
          validUntil: status === "current" && item.validUntil === "审核后确定" ? "2027-01-18" : item.validUntil,
        }
      : item));
    setDoc(current => current?.id === id ? { ...current, status } : current);
  };
  const createFAQFromDocument = (item) => setFaqOpen({
    createMode: "人工创建",
    articleId: item.id,
    articleVersion: item.version,
    reference: `${item.id} ${item.version}`,
    trigger: "从文章详情人工创建",
    scope: item.scope,
    risk: item.admission === "高风险政策" ? "高风险" : "低风险",
    reviewAt: item.admission === "高风险政策" ? "30 天后" : "180 天后",
  });
  const updateDocumentValidity = (id, validUntil) => {
    setDocuments(items => items.map(item => item.id === id ? { ...item, validUntil } : item));
    setValidityOpen(null);
  };
  const refreshDocument = (id) => {
    setDocuments(items => items.map(item => item.id === id ? { ...item, updated: "刚刚", change: "已重新读取当前来源；等待用途检查" } : item));
  };
  const addSource = (source) => {
    const nextId = Math.max(...documents.map(item => Number(item.id.replace(/\D/g, "")) || 0)) + 1;
    const created = {
      id: `DOC-${nextId}`,
      title: source.name || source.location,
      type: source.type,
      status: "synced",
      admission: "待判断",
      language: "待识别",
      scope: "待确认",
      version: "v1",
      updated: "刚刚",
      validUntil: "审核后确定",
      change: "首次同步，尚未判断用途",
      impact: "0 个当前引用内容",
      demand: "尚未关联问题信号",
      answerReason: "已同步，完成用途判断前不能用于回答",
      route: "完成审核前不可用于回答",
      ttl: "审核后确定",
      review: "首次来源全部人工检查",
    };
    setDocuments(items => [created, ...items]);
    setDoc(created);
    setSourceOpen(false);
  };

  if (sel) return <SkillDetail skill={sel} onBack={() => setSel(null)} />;

  const statusBadge = (s) => ({
    published: <Badge tone="green" dot="var(--green)">已发布</Badge>,
    review: <Badge tone="amber" dot="var(--amber)">审核中</Badge>,
    shadow: <Badge tone="purple" dot="var(--purple)">对照测试中</Badge>,
    blocked: <Badge tone="red" dot="var(--red)">禁止保存</Badge>,
  })[s.status];
  const primaryAction = tab === "skills"
    ? <button className="btn primary sm" onClick={() => openTarget("test", "regression")}><Icons.flask s={14} /> 运行测试</button>
    : tab === "review"
      ? <button className="btn primary sm" onClick={() => setFaqOpen({ createMode: "人工创建" })}><Icons.doc s={14} /> 新建常见问答</button>
      : <button className="btn primary sm" onClick={() => setSourceOpen(true)}><Icons.arrowUp s={14} /> 新增来源</button>;

  return (
    <div className="content-pad fadein">
      <window.PageHead title="训练" sub="管理文章、常见问答和处理流程。系统建议只生成待审稿。"
        right={primaryAction} />

      <window.Tabs active={tab} onChange={setTab} tabs={[
        { id: "documents", label: "文章" },
        { id: "review", label: "常见问答", count: faqs.length },
        { id: "skills", label: "处理流程" },
      ]} />

      {tab === "documents" && (
        <div className="captain-knowledge">
          <div className="captain-board-head">
            <div className="row" style={{ gap: 10 }}>
              <button className="captain-assistant">Anna <Icons.chevD s={13} /></button>
              <div className="captain-title">文章</div>
            </div>
            <div className="row captain-actions">
              <div className="captain-search"><Icons.search s={14} /><input aria-label="搜索来源文档" value={search} onChange={event => setSearch(event.target.value)} placeholder="搜索..." /></div>
              <button className="btn primary sm" onClick={() => setSourceOpen(true)}><Icons.arrowUp s={13} /> 新增来源</button>
            </div>
          </div>

          <div className="captain-filters">
            <select className="captain-filter" aria-label="筛选来源类型" value={typeFilter} onChange={event => setTypeFilter(event.target.value)}>
              <option value="all">全部来源类型</option>
              {[...new Set(documents.map(item => item.type))].map(value => <option key={value}>{value}</option>)}
            </select>
            <select className="captain-filter" aria-label="筛选回答类别" value={admissionFilter} onChange={event => setAdmissionFilter(event.target.value)}>
              <option value="all">全部回答类别</option>
              {[...new Set(documents.map(item => item.admission))].map(value => <option key={value}>{value}</option>)}
            </select>
            <select className="captain-filter" aria-label="筛选语言" value={languageFilter} onChange={event => setLanguageFilter(event.target.value)}>
              <option value="all">全部语言</option>
              {[...new Set(documents.map(item => item.language))].map(value => <option key={value}>{value}</option>)}
            </select>
            <select className="captain-filter" aria-label="筛选文章状态" value={statusFilter} onChange={event => setStatusFilter(event.target.value)}>
              <option value="all">全部状态</option>
              <option value="synced">已同步</option>
              <option value="current">用途已确认</option>
              <option value="needs-review">需要复核</option>
              <option value="expired">已过期</option>
              <option value="retired">已退出</option>
            </select>
            <select className="captain-filter" aria-label="筛选有效期" value={validityFilter} onChange={event => setValidityFilter(event.target.value)}>
              <option value="all">全部有效期</option>
              <option value="active">当前或待审核</option>
              <option value="attention">需要处理</option>
            </select>
          </div>

          <div className="captain-body">
            <div className="captain-list">
              {visibleDocuments.map(d => (
                <DocumentRow key={d.id} doc={d} active={activeDoc.id === d.id} onSelect={() => selectDocument(d)} onRelated={() => { selectDocument(d); setRelatedDoc(d.id); }} onRefresh={() => refreshDocument(d.id)} onStop={() => updateDocumentStatus(d.id, "retired")} />
              ))}
              {!visibleDocuments.length ? <window.EmptyState title="没有符合条件的文章" sub="调整搜索词或筛选条件后重试。" /> : null}

              <div className="captain-pagination">
                <span>显示第 1 - {visibleDocuments.length} 条，共 {visibleDocuments.length} 条</span>
                <div className="row" style={{ gap: 8 }}>
                  <button className="btn sm ghost" disabled><Icons.chevR s={13} style={{ transform: "rotate(180deg)" }} /></button>
                  <span className="mono">1 / 1</span>
                  <button className="btn sm ghost" disabled><Icons.chevR s={13} /></button>
                </div>
              </div>
            </div>

            <div className="captain-side">
              <DocumentDetail
                doc={activeDoc}
                faqs={faqs}
                gaps={CONVERSATION_GAPS}
                focusRelated={relatedDoc === activeDoc.id}
                onOpenReview={() => setTab("review")}
                onOpenSuggestion={(gap) => setGapOpen(gap)}
                onCreateFAQ={() => createFAQFromDocument(activeDoc)}
                onOpenFAQ={(faq) => setFaqOpen(faq)}
                onSetValidity={() => setValidityOpen(activeDoc)}
                onStatus={(status) => updateDocumentStatus(activeDoc.id, status)}
              />
            </div>
          </div>
        </div>
      )}

      {tab === "review" && (
        <>
          <div className="review-mobile-tabs">
            <button className={reviewPane === "faqs" ? "active" : ""} onClick={() => setReviewPane("faqs")}>常见问答</button>
            <button className={reviewPane === "suggestions" ? "active" : ""} onClick={() => setReviewPane("suggestions")}>系统建议</button>
          </div>
          <div className="captain-review-grid">
            <div className={"review-pane review-faqs" + (reviewPane === "faqs" ? " active" : "")}>
              <FAQListPanel
                faqs={faqs}
                onCreateFAQ={(f) => setFaqOpen(f)}
                onCreateVersion={createFAQVersion}
                onRunTests={() => openTarget("test", "regression")}
                onRejectRequest={(f) => setFaqOpen({ ...f, action: "reject" })}
                onMergeRequest={(f) => setFaqOpen({ ...f, action: "merge" })}
              />
            </div>
            <div className={"review-pane review-suggestions" + (reviewPane === "suggestions" ? " active" : "")}>
              <ConversationGapPanel
                gaps={CONVERSATION_GAPS}
                onOpenGap={setGapOpen}
              />
            </div>
          </div>
        </>
      )}

      {tab === "skills" && (
        <div className="card">
          <div className="card-head"><Icons.layers s={15} /><span className="card-title">处理流程</span><span className="card-sub">{SKILLS.length} 个只读处理流程</span></div>
          <table className="tbl">
            <thead><tr><th>处理流程</th><th>场景</th><th>重新测试通过率</th><th>安全规则</th><th>状态</th><th></th></tr></thead>
            <tbody>
              {SKILLS.map(s => {
                const detailReady = s.key === "kyc-failed";
                return (
                <tr key={s.id} style={{ cursor: detailReady ? "pointer" : "default" }} onClick={detailReady ? () => setSel(s) : undefined}>
                  <td><div style={{ fontWeight: 600 }}>{s.name}</div><div className="muted" style={{ fontSize: 11 }}>研发标识：<span className="mono">{s.key}</span> · {s.id} · {s.ver}</div></td>
                  <td><div>{s.uc}</div><div className="muted" style={{ fontSize: 11.5, marginTop: 2 }}>{s.level} · {s.mode}</div></td>
                  <td><div className="row" style={{ gap: 7 }}><div style={{ width: 56 }}><window.Meter pct={s.pass} color={s.pass >= 90 ? "var(--green)" : s.pass >= 80 ? "var(--blue)" : "var(--orange)"} /></div><span style={{ fontSize: 12, fontWeight: 600 }}>{s.pass}%</span></div></td>
                  <td>{s.redlines.length ? <span className="status" title={s.redlines.join(" · ")}><Icons.lock s={12} className="status-ico" />{s.redlines.length} 条安全规则</span> : <span className="muted">—</span>}</td>
                  <td>{statusBadge(s)}</td>
                  <td>{detailReady ? <Icons.chevR s={15} className="muted" /> : <span className="muted" style={{ fontSize: 11.5 }}>仅列表</span>}</td>
                </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      )}
      {sourceOpen ? <SourceModal onClose={() => setSourceOpen(false)} onSave={addSource} /> : null}
      {validityOpen ? <ValidityModal doc={validityOpen} onClose={() => setValidityOpen(null)} onSave={(value) => updateDocumentValidity(validityOpen.id, value)} /> : null}
      {faqOpen ? <FAQModal seed={faqOpen} targets={faqs} onClose={() => setFaqOpen(null)} onSave={saveFAQ} onCreateVersion={createFAQVersion} onReject={(id) => { rejectFAQ(id); setFaqOpen(null); }} onMerge={(id) => { mergeFAQ(id); setFaqOpen(null); }} /> : null}
      {gapOpen ? (
        <SuggestionDetailModal
          gap={gapOpen}
          faqs={faqs}
          onClose={() => setGapOpen(null)}
          onOpenFAQ={(faq) => { setGapOpen(null); setFaqOpen(faq); }}
          onOpenFlow={() => {
            setGapOpen(null);
            setTab("skills");
          }}
          onManualCreate={(g) => {
            setGapOpen(null);
            setFaqOpen({
              createMode: "人工创建",
              q: g.intent,
              scope: g.scope,
              trigger: g.signal,
              reference: `${g.id} · ${g.basis}`,
              decision: g.decision,
              risk: "高风险",
              reviewAt: "30 天后",
            });
          }}
        />
      ) : null}
    </div>
  );
}

function DocumentDetail({ doc, faqs, gaps, focusRelated, onOpenReview, onOpenSuggestion, onCreateFAQ, onOpenFAQ, onSetValidity, onStatus }) {
  const { Badge } = window;
  const Icons = window.Icons;
  const [showImpact, setShowImpact] = useState(false);
  const related = faqs.filter(f => f.articleId === doc.id);
  const relatedGap = gaps.find(g => g.docId === doc.id && (g.faqId || g.manual));
  return (
    <div className="card">
      <div className="card-head"><Icons.doc s={15} /><span className="card-title">来源详情</span><span className="card-sub">{doc.id}</span></div>
      <div className="card-pad">
        <div style={{ fontWeight: 600 }}>{doc.title}</div>
        <dl className="kv" style={{ margin: "12px 0" }}>
          <dt>状态</dt><dd><SourceStatusBadge status={doc.status} admission={doc.admission} /></dd>
          <dt>内容来源</dt><dd>{doc.type}</dd>
          <dt>回答类别</dt><dd>{doc.admission}</dd>
          <dt>语言</dt><dd>{doc.language}</dd>
          <dt>适用范围</dt><dd>{doc.scope}</dd>
          <dt>允许的回答方式</dt><dd>{doc.route}</dd>
          <dt>来源版本</dt><dd className="mono">{doc.version}</dd>
          <dt>版本变化</dt><dd>{doc.change}</dd>
          <dt>更新时间</dt><dd>{doc.updated}</dd>
          <dt>有效期</dt><dd>{doc.validUntil}</dd>
          <dt>复核周期</dt><dd>{doc.ttl}</dd>
          <dt>审核方式</dt><dd>{doc.review}</dd>
          <dt>用途判断原因</dt><dd>{doc.answerReason}</dd>
          <dt>真实问题信号</dt><dd>{doc.demand}</dd>
          <dt>引用影响</dt><dd>{doc.impact}</dd>
          <dt>引用该文章的问答</dt><dd><b>{related.length}</b></dd>
        </dl>
        <div className="doc-actions">
          {doc.status === "synced"
            ? <button className="btn primary sm" onClick={() => onStatus("needs-review")}><Icons.shield s={13} /> 送人工复核</button>
            : doc.status === "needs-review"
              ? <button className="btn primary sm" onClick={() => onStatus("current")}><Icons.checkCircle s={13} /> 确认用途</button>
              : doc.status === "current"
                ? <button className="btn sm" onClick={onSetValidity}><Icons.clock s={13} /> 设置有效期</button>
                : doc.status === "expired"
                  ? <button className="btn sm" onClick={() => onStatus("needs-review")}><Icons.refresh s={13} /> 更新后重新复核</button>
                  : null}
          <button className="btn sm" onClick={() => setShowImpact(value => !value)}><Icons.link s={13} /> {showImpact ? "收起受影响内容" : "查看受影响内容"}</button>
          {doc.status === "current" || doc.admission === "高风险政策"
            ? <button className="btn sm" onClick={onCreateFAQ}><Icons.doc s={13} /> 人工创建问答</button>
            : null}
          {doc.status !== "retired" ? <button className="btn sm danger" onClick={() => onStatus("retired")}><Icons.x s={13} /> 停止使用</button> : null}
          <button className="btn sm" disabled={!relatedGap} onClick={relatedGap ? () => onOpenSuggestion(relatedGap) : undefined}>
            {relatedGap ? <Icons.chevR s={13} /> : <Icons.lock s={13} />}
            {relatedGap ? "在系统建议中查看" : "当前无系统建议"}
          </button>
        </div>
        {!relatedGap ? (
          <div className="note" style={{ marginTop: 10 }}>
            <Icons.shield s={13} style={{ flexShrink: 0 }} />
            {doc.admission === "高风险政策"
              ? "文章本身不能放行高风险结论。只能由人创建问答；超出当前已发布问答或流程的适用范围时不支持回答。"
              : doc.status === "current"
                ? "当前没有系统建议；低风险文章仍可带版本引用回答，也可以由人创建问答。"
                : "该来源的用途尚未确认，或已经转实时来源 / 停止使用。"}
          </div>
        ) : null}
        {doc.status === "current" && doc.route.includes("低风险引用") ? (
          <div className="note" style={{ marginTop: 10 }}>
            <Icons.checkCircle s={13} style={{ flexShrink: 0, color: "var(--green)" }} />
            当前来源可支撑低频低风险问题回答，并展示来源版本；候选问答用于高频、易错或多步骤问题，不是回答覆盖的前置条件。
          </div>
        ) : null}
        {showImpact || focusRelated ? <div className={"related-faq" + (focusRelated ? " focus" : "")}>
          <div className="related-faq-head">关联常见问答</div>
          {related.length ? related.map(f => (
            <button key={f.id} className="related-faq-row" onClick={() => onOpenFAQ(f)}>
              <span>{faqDisplayId(f)} · {f.q}</span>
              {f.status === "published" ? <Badge tone="green">已发布</Badge> : f.status === "disabled" ? <Badge tone="red">已停用</Badge> : <Badge tone="amber">待审核</Badge>}
            </button>
          )) : <span className="muted" style={{ fontSize: 12.5 }}>暂无关联常见问答。</span>}
        </div> : null}
      </div>
    </div>
  );
}

function DocumentRow({ doc, active, onSelect, onRelated, onRefresh, onStop }) {
  const Icons = window.Icons;
  const [menu, setMenu] = useState(false);
  return (
    <button className={"captain-doc-row" + (active ? " active" : "")} onClick={onSelect}>
      <div className="captain-doc-main">
        <div className="captain-doc-title">{doc.title}</div>
        <div className="captain-doc-meta">
          <span><Icons.globe s={12} /> {doc.language}</span>
          <span><Icons.link s={12} /> {doc.type}</span>
          <span><Icons.shield s={12} /> {doc.admission}</span>
          <span className="mono">{doc.id}</span>
        </div>
      </div>
      <div className="captain-doc-stats">
        <SourceStatusBadge status={doc.status} admission={doc.admission} />
        <span className="mono muted">{doc.version}</span>
        <span className="muted">{doc.scope}</span>
        <span className="muted">{doc.impact}</span>
        <span className="muted"><Icons.refresh s={12} /> 更新于 {doc.updated}</span>
        <span className="captain-menu-wrap" onClick={(e) => { e.stopPropagation(); setMenu(!menu); }}>
          <span className="captain-more"><Icons.sliders s={13} /></span>
          {menu ? (
            <span className="captain-popover">
              <span onClick={(e) => { e.stopPropagation(); setMenu(false); onRelated(); }}><Icons.link s={13} /> 查看关联常见问答</span>
              <span onClick={(e) => { e.stopPropagation(); setMenu(false); onRefresh(); }}><Icons.refresh s={13} /> 立即刷新</span>
              <span className="danger" onClick={(e) => { e.stopPropagation(); setMenu(false); onStop(); }}><Icons.x s={13} /> 停止使用</span>
            </span>
          ) : null}
        </span>
      </div>
    </button>
  );
}

function SourceStatusBadge({ status, admission }) {
  const { Badge } = window;
  const map = {
    synced: <Badge tone="gray">已同步</Badge>,
    current: <Badge tone="green" dot="var(--green)">用途已确认</Badge>,
    "needs-review": <Badge tone="amber" dot="var(--amber)">需要复核</Badge>,
    expired: <Badge tone="red" dot="var(--red)">已过期</Badge>,
    retired: <Badge tone="gray">已退出</Badge>,
  };
  return map[status] || <Badge tone="gray">{status}</Badge>;
}

function FAQListPanel({ faqs, onCreateFAQ, onCreateVersion, onRunTests, onRejectRequest, onMergeRequest }) {
  const { Badge } = window;
  const Icons = window.Icons;
  const [statusFilter, setStatusFilter] = useState("all");
  const counts = {
    all: faqs.length,
    pending: faqs.filter(f => f.status === "pending").length,
    published: faqs.filter(f => f.status === "published").length,
    disabled: faqs.filter(f => f.status === "disabled").length,
  };
  const visibleFaqs = statusFilter === "all" ? faqs : faqs.filter(f => f.status === statusFilter);
  return (
    <div className="card">
      <div className="card-head"><Icons.flag s={15} /><span className="card-title">常见问答列表</span><span className="card-sub">共 {counts.all} 条 · {counts.pending} 条待审核</span><button className="btn sm ghost" style={{ marginLeft: "auto" }} onClick={() => onCreateFAQ({ createMode: "人工创建" })}><Icons.doc s={13} /> 新建常见问答</button></div>
      <div className="card-pad" style={{ display: "flex", flexDirection: "column", gap: 10 }}>
        <div className="faq-list-toolbar" aria-label="筛选常见问答状态">
          {[
            ["all", "全部"],
            ["pending", "待审核"],
            ["published", "已发布"],
            ["disabled", "已停用"],
          ].map(([id, label]) => (
            <button key={id} className={"pill" + (statusFilter === id ? " active" : "")} onClick={() => setStatusFilter(id)}>
              {label} <span className="mono">{counts[id]}</span>
            </button>
          ))}
        </div>
        <div className="note">
          <Icons.refresh s={13} style={{ flexShrink: 0 }} />
          这里展示全部常见问答。问答状态描述当前线上是否可用；编辑、需要重测、测试通过和等待发布是本次新版本的处理步骤。
        </div>
        {visibleFaqs.map(f => (
          <div key={f.id} className="diff-card">
            <div className="spread" style={{ marginBottom: 6 }}>
              <span className="mono" style={{ fontWeight: 600, fontSize: 12 }}>{faqDisplayId(f)}</span>
              {f.status === "published" ? <Badge tone="green">已发布</Badge> : f.status === "disabled" ? <Badge tone="red">已停用</Badge> : <Badge tone="amber">{f.draftStep || "待审核"}</Badge>}
            </div>
            <div style={{ fontWeight: 600, fontSize: 13 }}>{f.q}</div>
            {f.status === "pending" ? <div className="faq-version-line"><span>当前线上：{f.liveVersion || "无"} · {f.liveStatus || "尚未发布"}</span><span>本次草稿：{f.version} · {f.draftStep || "待审核"}</span></div> : null}
            <div className="muted" style={{ fontSize: 12, marginTop: 5 }}>{f.createMode} · {f.articleId ? `引用 ${f.articleId} ${f.articleVersion}` : "未引用文章"} · {f.reference}</div>
            <div className="muted" style={{ fontSize: 12, marginTop: 5 }}>{f.intent} · {f.evidence}</div>
            <div className="faq-proof">
              <span><Icons.shield s={12} /> 风险：{f.risk}</span>
              <span><Icons.flag s={12} /> 触发：{f.trigger}</span>
              <span><Icons.clock s={12} /> 复核：{f.reviewAt}</span>
            </div>
            {f.qualified ? (
              <div className="note" style={{ marginTop: 9 }}><Icons.checkCircle s={13} style={{ flexShrink: 0 }} /> 24 小时结果确认：{f.qualified}；{f.excluded}。系统只生成这一条候选，不逐会话生成。</div>
            ) : null}
            {f.createMode === "系统建议" ? (
              <div className="suggestion-proof">
                <span><b>为何可起草：</b>{f.decision}</span>
                <span><b>去重结果：</b>{f.dedupe}</span>
              </div>
            ) : null}
            {f.status === "disabled" ? (
              <div className="note" style={{ marginTop: 9 }}><Icons.lock s={13} style={{ flexShrink: 0 }} /> {f.disableReason}。当前不可用于回答。</div>
            ) : null}
            {f.status === "pending" && (
              <div className="faq-actions">
                <button className="btn sm" onClick={onRunTests}><Icons.check s={13} /> 确认内容并测试</button>
                <button className="btn sm ghost" onClick={() => onCreateFAQ(f)}>保存修改</button>
                <button className="btn sm ghost" onClick={() => onMergeRequest(f)}>合并到已有问答</button>
                <button className="btn sm ghost" onClick={() => onRejectRequest(f)}>拒绝候选</button>
              </div>
            )}
            {f.status === "published" && (
              <div className="faq-actions">
                <button className="btn sm ghost" onClick={() => onCreateFAQ(f)}><Icons.doc s={13} /> 查看问答</button>
                <button className="btn sm" onClick={onRunTests}><Icons.flask s={13} /> 查看测试记录</button>
                <button className="btn sm ghost" onClick={() => onCreateVersion(f)}><Icons.refresh s={13} /> 创建新版本</button>
              </div>
            )}
            {f.status === "disabled" && (
              <div className="faq-actions">
                <button className="btn sm ghost" onClick={() => onCreateFAQ(f)}><Icons.doc s={13} /> 查看问答</button>
                <button className="btn sm ghost" onClick={() => onCreateVersion(f)}><Icons.refresh s={13} /> 创建新版本</button>
              </div>
            )}
          </div>
        ))}
        {!visibleFaqs.length ? <window.EmptyState title="没有符合条件的常见问答" sub="切换状态筛选查看其他问答。" /> : null}
      </div>
    </div>
  );
}

function ConversationGapPanel({ gaps, onOpenGap }) {
  const { Badge } = window;
  const Icons = window.Icons;
  return (
    <div className="card">
      <div className="card-head"><Icons.search s={15} /><span className="card-title">系统建议</span><span className="card-sub">来自已解决会话、文章变化和真实问题信号</span></div>
      <div className="suggestion-flow">
        {[
          ["1", "归并问题", "按问题意思、语言和适用范围合并信号"],
          ["2", "判断为什么现在处理", "问题量、负向反馈、文章变化或稳定已解决模式"],
          ["3", "检查答案依据", "负向信号不等于答案；只有稳定低风险答案才起草"],
          ["4", "过滤边界", "个案、动态、隐私、高风险和答案冲突分别处理"],
          ["5", "匹配现有问答", "合并待审稿、更新同一问答，或确认无需变化"],
          ["6", "选择问答或处理流程", "单轮稳定表达维护问答；多步、工具分支或复杂转人工条件维护处理流程"],
          ["7", "检查容量并给出结果", "说明内容变化、人工下一步和再次判断条件"],
        ].map(([n, title, desc]) => (
          <div key={n} className="suggestion-step"><span>{n}</span><div><b>{title}</b><small>{desc}</small></div></div>
        ))}
      </div>
      <div className="note" style={{ margin: "0 14px 10px" }}><Icons.sliders s={13} style={{ flexShrink: 0 }} /> 系统不使用综合分数。容量只决定本轮是否创建待审工作，不会把本来不安全或没有依据的内容变成候选。</div>
      <div className="note" style={{ margin: "0 14px 10px" }}><Icons.alert s={13} style={{ flexShrink: 0 }} /> 24 小时和至少 2 条独立一致会话只是 L1 初始去噪参数。系统建议不会直接修改或发布问答；本轮因容量不足未进入队列的问题，最迟在下一个工作日补充队列时自动重评。</div>
      <div className="note" style={{ margin: "0 14px 10px" }}><Icons.lock s={13} style={{ flexShrink: 0 }} /> 无法安全脱敏或无法剥离个案事实时只保留汇总信号并“不生成”；脱敏后仍有可泛化模式时，重新按问题组判断。</div>
      <div className="gap-list">
        {gaps.map(g => (
          <button key={g.id} className="gap-item" onClick={() => onOpenGap(g)}>
            <div className="gap-main">
              <div className="gap-title">{g.intent}</div>
              <div className="gap-meta"><span className="mono">{g.id}</span><span>{g.priority}</span><span>{g.language} · {g.scope}</span><span>{g.signal}</span></div>
              <div className="gap-decision"><span><b>答案依据：</b>{g.basis}</span><span><b>系统判断：</b>{g.decision}</span></div>
            </div>
            <div className="gap-action">
              <span>{g.action}</span>
              {g.state === "generated"
                ? <Badge tone="green">新建</Badge>
                : g.state === "update"
                  ? <Badge tone="blue">更新</Badge>
                  : g.state === "merged"
                    ? <Badge tone="blue">合并</Badge>
                    : g.state === "manual"
                      ? <Badge tone="amber">提示人工创建</Badge>
                      : <Badge tone="gray">不生成</Badge>}
            </div>
          </button>
        ))}
      </div>
    </div>
  );
}

function SuggestionDetailModal({ gap, faqs, onClose, onOpenFAQ, onManualCreate, onOpenFlow }) {
  const Icons = window.Icons;
  const { Badge } = window;
  const [split, setSplit] = useState(false);
  const candidate = faqs.find(f => f.id === gap.faqId);
  const resultTone = gap.state === "generated"
    ? "green"
    : gap.state === "update" || gap.state === "merged"
      ? "blue"
      : gap.state === "manual"
        ? "amber"
        : "gray";
  const resultLabel = gap.state === "generated"
    ? "新建"
    : gap.state === "update"
      ? "更新"
      : gap.state === "merged"
        ? "合并"
        : gap.state === "manual"
          ? "提示人工创建"
          : "不生成";

  return (
    <div className="modal-scrim" onClick={onClose}>
      <div className="modal faq-modal" onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <div><div style={{ fontWeight: 700, fontSize: 15 }}>系统建议判断</div><div className="muted" style={{ fontSize: 12.5, marginTop: 4 }}>{gap.id} · {gap.intent}</div></div>
          <button className="btn ghost sm" aria-label="关闭系统建议详情" onClick={onClose}><Icons.x s={16} /></button>
        </div>
        <div className="modal-body">
          <dl className="kv">
            <dt>问题归并</dt><dd>{gap.language} · {gap.scope}</dd>
            <dt>为什么现在处理</dt><dd>{gap.signal}</dd>
            <dt>答案依据</dt><dd>{gap.basis}</dd>
            <dt>排除与边界</dt><dd>{gap.exclude}</dd>
            <dt>匹配现有问答</dt><dd>{gap.match}</dd>
            <dt>维护对象判断</dt><dd>{gap.necessity}</dd>
            <dt>推荐维护</dt><dd>{gap.target || "常见问答"}</dd>
            <dt>队列容量</dt><dd>{gap.capacity}</dd>
            <dt>最终结果</dt><dd><div className="row" style={{ gap: 8, flexWrap: "wrap" }}><Badge tone={resultTone}>{resultLabel}</Badge><span>{gap.action}</span></div></dd>
            <dt>人工下一步</dt><dd>{gap.nextAction}</dd>
            <dt>再次判断</dt><dd>{gap.recheck}</dd>
          </dl>
          <div className="note" style={{ marginTop: 14 }}><Icons.shield s={13} style={{ flexShrink: 0 }} /> 失败、点踩、转人工和事故只能说明需要处理。没有当前文章或一致的已解决会话作为答案依据时，系统不会起草答案。</div>
          {split ? (
            <div className="gate" style={{ marginTop: 14 }}>
              <div className="gate-ico"><Icons.refresh s={16} /></div>
              <div style={{ flex: 1 }}>该问题组已退回重新归并。系统不会从当前分组生成或更新问答。</div>
              <Badge tone="amber">重新判断</Badge>
            </div>
          ) : null}
        </div>
        <div className="modal-foot">
          <button className="btn sm ghost" onClick={() => setSplit(true)} disabled={split}><Icons.sliders s={13} /> 拆分问题组</button>
          <div className="row" style={{ gap: 8 }}>
            <button className="btn sm" onClick={onClose}>关闭</button>
            {candidate ? <button className="btn primary sm" onClick={() => onOpenFAQ(candidate)}><Icons.chevR s={13} /> {gap.state === "merged" ? "打开已合并问答" : "打开待审核问答"}</button> : null}
            {gap.manual && gap.target !== "处理流程" ? <button className="btn primary sm" onClick={() => onManualCreate(gap)}><Icons.doc s={13} /> 人工创建问答</button> : null}
            {gap.manual && gap.target?.includes("处理流程") ? <button className="btn primary sm" onClick={onOpenFlow}><Icons.layers s={13} /> 前往处理流程</button> : null}
          </div>
        </div>
      </div>
    </div>
  );
}

function SourceModal({ onClose, onSave }) {
  const Icons = window.Icons;
  const [type, setType] = useState("网页地址");
  const [location, setLocation] = useState("");
  const [name, setName] = useState("");
  return (
    <div className="modal-scrim" onClick={onClose}>
      <div className="modal source-modal" onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <div><div style={{ fontWeight: 700, fontSize: 15 }}>新增来源</div><div className="muted" style={{ fontSize: 12.5, marginTop: 4 }}>同步网页、文档文件、帮助中心文章或标准处理流程，等待内容审核。</div></div>
          <button className="btn ghost sm" aria-label="关闭新增来源弹窗" onClick={onClose}><Icons.x s={16} /></button>
        </div>
        <div className="modal-body">
          <label className="form-field"><span>来源类型</span><select value={type} onChange={event => setType(event.target.value)}><option>网页地址</option><option>文档文件</option><option>帮助中心</option><option>标准处理流程</option></select></label>
          <label className="form-field"><span>URL 或文件</span><input value={location} onChange={event => setLocation(event.target.value)} placeholder="https://support.example.com/article" /></label>
          <label className="form-field"><span>名称（可选）</span><input value={name} onChange={event => setName(event.target.value)} placeholder="来源名称" /></label>
          <div className="note"><Icons.lock s={14} style={{ flexShrink: 0 }} /> 新来源先同步并审核。允许使用前不能回答客户。</div>
        </div>
        <div className="modal-foot">
          <span className="muted" style={{ fontSize: 12 }}>新增来源不会发布回答。</span>
          <div className="row" style={{ gap: 8 }}><button className="btn sm" onClick={onClose}>取消</button><button className="btn primary sm" disabled={!location.trim()} onClick={() => onSave({ type, location: location.trim(), name: name.trim() })}>新增来源</button></div>
        </div>
      </div>
    </div>
  );
}

function ValidityModal({ doc, onClose, onSave }) {
  const Icons = window.Icons;
  const initial = /^\d{4}-\d{2}-\d{2}$/.test(doc.validUntil) ? doc.validUntil : "";
  const [value, setValue] = useState(initial);
  return (
    <div className="modal-scrim" onClick={onClose}>
      <div className="modal source-modal" onClick={event => event.stopPropagation()}>
        <div className="modal-head">
          <div><div style={{ fontWeight: 700, fontSize: 15 }}>设置文章有效期</div><div className="muted" style={{ fontSize: 12.5, marginTop: 4 }}>{doc.id} · {doc.title}</div></div>
          <button className="btn ghost sm" aria-label="关闭有效期弹窗" onClick={onClose}><Icons.x s={16} /></button>
        </div>
        <div className="modal-body">
          <label className="form-field"><span>停止使用日期</span><input type="date" value={value} onChange={event => setValue(event.target.value)} /></label>
          <div className="note"><Icons.clock s={13} style={{ flexShrink: 0 }} /> 到期后文章停止用于回答。继续使用需更新并复核。</div>
        </div>
        <div className="modal-foot">
          <span className="muted" style={{ fontSize: 12 }}>有效期变化进入文章操作记录。</span>
          <div className="row" style={{ gap: 8 }}><button className="btn sm" onClick={onClose}>取消</button><button className="btn primary sm" disabled={!value} onClick={() => onSave(value)}>保存有效期</button></div>
        </div>
      </div>
    </div>
  );
}

function FAQModal({ seed, targets, onClose, onSave, onCreateVersion, onReject, onMerge }) {
  const Icons = window.Icons;
  const { Badge } = window;
  const readOnly = seed?.status === "published" || seed?.status === "disabled";
  const [rejectReason, setRejectReason] = useState("");
  const mergeTargets = (targets || []).filter(f => f.id !== seed?.id && f.status === "pending");
  const [mergeTarget, setMergeTarget] = useState(mergeTargets[0]?.id || "");
  const [risk, setRisk] = useState(seed?.risk && seed.risk !== "低风险" ? "高风险" : "低风险");
  const [reviewAt, setReviewAt] = useState(seed?.reviewAt || (seed?.risk && seed.risk !== "低风险" ? "30 天后" : "180 天后"));
  const [question, setQuestion] = useState(seed?.q || "");
  const [answer, setAnswer] = useState(seed?.answer || "");
  const [scope, setScope] = useState(seed?.scope || "");
  const [excludes, setExcludes] = useState(seed?.excludes || "");
  const [reference, setReference] = useState(seed?.reference || "");
  const [articleReference, setArticleReference] = useState([seed?.articleId, seed?.articleVersion].filter(Boolean).join(" "));
  const [trigger, setTrigger] = useState(seed?.trigger || "人工创建");
  const [allowedScope, setAllowedScope] = useState(seed?.evidence || "");
  const [toolBranches, setToolBranches] = useState(seed?.toolBranches || "");
  const [fallback, setFallback] = useState(seed?.fallback || "当前无法确认或承诺；必要澄清或转人工。具体政策否定必须由已发布内容支持。");
  const [forbidden, setForbidden] = useState(seed?.forbidden || "");

  if (seed?.action === "reject") {
    return (
      <div className="modal-scrim" onClick={onClose}>
        <div className="modal faq-modal" onClick={e => e.stopPropagation()}>
          <div className="modal-head">
            <div><div style={{ fontWeight: 700, fontSize: 15 }}>拒绝候选问答</div><div className="muted" style={{ fontSize: 12.5, marginTop: 4 }}>{faqDisplayId(seed)} · 拒绝记录不会成为问答状态。</div></div>
            <button className="btn ghost sm" aria-label="关闭拒绝候选弹窗" onClick={onClose}><Icons.x s={16} /></button>
          </div>
          <div className="modal-body">
            <label className="form-field"><span>拒绝原因</span><textarea value={rejectReason} onChange={e => setRejectReason(e.target.value)} placeholder="说明答案错误、范围不清、与现有问答重复或当前不值得维护的原因..." /></label>
            <div className="note"><Icons.refresh s={13} style={{ flexShrink: 0 }} /> 相同信号不会再次生成；只有文章、会话模式或人工输入发生实质变化时才重新评估。</div>
          </div>
          <div className="modal-foot">
            <span className="muted" style={{ fontSize: 12 }}>该候选将从待审核列表移除，原因保留在处理历史。</span>
            <div className="row" style={{ gap: 8 }}><button className="btn sm" onClick={onClose}>取消</button><button className="btn danger sm" disabled={!rejectReason.trim()} onClick={() => onReject(seed.id, rejectReason.trim())}>记录拒绝</button></div>
          </div>
        </div>
      </div>
    );
  }

  if (seed?.action === "merge") {
    return (
      <div className="modal-scrim" onClick={onClose}>
        <div className="modal faq-modal" onClick={e => e.stopPropagation()}>
          <div className="modal-head">
            <div><div style={{ fontWeight: 700, fontSize: 15 }}>合并重复候选</div><div className="muted" style={{ fontSize: 12.5, marginTop: 4 }}>{faqDisplayId(seed)} · 只能合并到已有待审稿，不修改已发布版本。</div></div>
            <button className="btn ghost sm" aria-label="关闭合并候选弹窗" onClick={onClose}><Icons.x s={16} /></button>
          </div>
          <div className="modal-body">
            <label className="form-field"><span>合并到待审稿</span><select value={mergeTarget} onChange={e => setMergeTarget(e.target.value)}>{mergeTargets.map(f => <option key={f.id} value={f.id}>{faqDisplayId(f)} · {f.q}</option>)}</select></label>
            <div className="note"><Icons.link s={13} style={{ flexShrink: 0 }} /> 当前候选的触发信号和参考材料会加入目标问答，当前重复候选从待审核列表移除，处理历史保留本次合并。</div>
          </div>
          <div className="modal-foot">
            <span className="muted" style={{ fontSize: 12 }}>合并不会自动发布或改变目标问答状态。</span>
            <div className="row" style={{ gap: 8 }}><button className="btn sm" onClick={onClose}>取消</button><button className="btn primary sm" disabled={!mergeTarget} onClick={() => onMerge(seed.id, mergeTarget)}><Icons.link s={13} /> 确认合并</button></div>
          </div>
        </div>
      </div>
    );
  }

  const origin = seed?.createMode || "人工创建";
  const saveDraft = () => {
    const [articleId = "", articleVersion = ""] = articleReference.trim().split(/\s+/, 2);
    onSave({
      ...seed,
      q: question.trim(),
      answer: answer.trim(),
      scope: scope.trim(),
      excludes: excludes.trim(),
      reference: reference.trim(),
      articleId,
      articleVersion,
      trigger: trigger.trim(),
      risk,
      reviewAt,
      evidence: allowedScope.trim(),
      toolBranches: toolBranches.trim(),
      fallback: fallback.trim(),
      forbidden: forbidden.trim(),
      status: seed?.status || "pending",
    });
  };
  return (
    <div className="modal-scrim" onClick={onClose}>
      <div className="modal faq-modal" onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <div><div style={{ fontWeight: 700, fontSize: 15 }}>{readOnly ? `查看 ${faqDisplayId(seed)}` : seed?.id ? `编辑 ${faqDisplayId(seed)}` : seed?.logicalId ? `创建 ${seed.logicalId} 的新版本` : "创建待审核问答"}</div><div className="muted" style={{ fontSize: 12.5, marginTop: 4 }}>{readOnly ? "已发布和已停用版本只读；需要修改时创建待审核的新版本。" : `${seed?.liveVersion ? `当前线上 ${seed.liveVersion} · ${seed.liveStatus}；` : ""}测试通过并发布后，本次新版本才生效。`}</div></div>
          <button className="btn ghost sm" aria-label="关闭常见问答弹窗" onClick={onClose}><Icons.x s={16} /></button>
        </div>
        <div className="modal-body">
          <div className="form-grid">
            <div className="form-field"><span>产生方式</span><div className="readonly-field"><Badge tone={origin === "系统建议" ? "blue" : "gray"}>{origin}</Badge><small>由进入路径确定，不能修改</small></div></div>
            <label className="form-field"><span>下次复核</span><input value={reviewAt} readOnly={readOnly} onChange={e => setReviewAt(e.target.value)} /></label>
          </div>
          {origin === "系统建议" ? (
            <div className="form-field"><span>风险</span><div className="readonly-field"><Badge tone="green">低风险</Badge><small>高风险内容不会由系统起草</small></div></div>
          ) : (
            <label className="form-field"><span>风险</span><select value={risk} disabled={readOnly} onChange={e => { const nextRisk = e.target.value; setRisk(nextRisk); if (nextRisk === "高风险" && reviewAt === "180 天后") setReviewAt("30 天后"); }}><option>低风险</option><option>高风险</option></select></label>
          )}
          {origin === "系统建议" ? (
            <div className="suggestion-proof" style={{ marginBottom: 14 }}>
              <span><b>生成原因：</b>{seed.trigger}</span>
              <span><b>为何可起草：</b>{seed.decision}</span>
              <span><b>去重结果：</b>{seed.dedupe}</span>
            </div>
          ) : null}
          <label className="form-field"><span>问题</span><input value={question} readOnly={readOnly} onChange={e => setQuestion(e.target.value)} placeholder="客户会怎么问？" /></label>
          <label className="form-field"><span>答案</span><textarea value={answer} readOnly={readOnly} onChange={e => setAnswer(e.target.value)} placeholder="填写待审核的客户答案..." /></label>
          <div className="form-grid">
            <label className="form-field"><span>适用条件</span><input value={scope} readOnly={readOnly} onChange={e => setScope(e.target.value)} placeholder="产品、地区、资产、网络或问题范围" /></label>
            <label className="form-field"><span>不适用条件</span><input value={excludes} readOnly={readOnly} onChange={e => setExcludes(e.target.value)} placeholder="不能外推的情况" /></label>
          </div>
          <div className="form-grid">
            <label className="form-field"><span>参考材料（可选）</span><input value={reference} readOnly={readOnly} onChange={e => setReference(e.target.value)} placeholder="可留空，或填写脱敏会话、文章、流程" /></label>
            <label className="form-field"><span>引用文章和版本（可选）</span><input value={articleReference} readOnly={readOnly} onChange={e => setArticleReference(e.target.value)} placeholder="例如 DOC-18 v5；答案明确依赖文章时填写" /></label>
          </div>
          <label className="form-field"><span>创建原因</span><input value={trigger} readOnly={readOnly} onChange={e => setTrigger(e.target.value)} placeholder="事故、未解决、重复追问、点踩或业务要求" /></label>
          {risk === "高风险" ? (
            <div className="four-el" style={{ marginTop: 14 }}>
              <div className="four-el-h">高风险回答边界</div>
              <div className="col" style={{ gap: 10 }}>
                <label className="form-field"><span>允许回答的范围</span><textarea value={allowedScope} readOnly={readOnly} onChange={e => setAllowedScope(e.target.value)} placeholder="写清允许表达的政策结论，不支持的范围直接停止。" /></label>
                <label className="form-field"><span>需要查询的工具与结果分支</span><textarea value={toolBranches} readOnly={readOnly} onChange={e => setToolBranches(e.target.value)} placeholder="例如：处理中只能说明仍在处理；已拒绝只能说明未通过；超时不得猜测。" /></label>
                <label className="form-field"><span>范围外、失败或对象不匹配</span><input value={fallback} readOnly={readOnly} onChange={e => setFallback(e.target.value)} /></label>
                <label className="form-field"><span>禁止表达</span><textarea value={forbidden} readOnly={readOnly} onChange={e => setForbidden(e.target.value)} placeholder="不得承诺批准、退款、找回、赔付、责任或完成时间。" /></label>
              </div>
            </div>
          ) : null}
          <div className="faq-proof" style={{ marginTop: 14 }}>
            <span><Icons.doc s={12} /> 文章引用可选</span>
            <span><Icons.link s={12} /> 依赖文章时记录版本和段落</span>
            <span><Icons.flask s={12} /> 保存后重新测试</span>
          </div>
          <div className="gate" style={{ marginTop: 14 }}>
            <div className="gate-ico"><Icons.flag s={17} /></div>
            <div style={{ flex: 1, fontWeight: 500 }}>保存后仍是待审核问答；确认内容、测试通过并发布后才生效。</div>
            <Badge tone="amber">待审核</Badge>
          </div>
        </div>
        <div className="modal-foot">
          <span className="muted" style={{ fontSize: 12 }}>{readOnly ? "当前版本不能直接修改。" : "待审问答不得用于客户回答。"}</span>
          {readOnly ? (
            <div className="row" style={{ gap: 8 }}><button className="btn sm" onClick={onClose}>关闭</button><button className="btn primary sm" onClick={() => onCreateVersion(seed)}><Icons.refresh s={13} /> 创建新版本</button></div>
          ) : (
            <div className="row" style={{ gap: 8 }}><button className="btn sm" onClick={onClose}>取消</button><button className="btn primary sm" disabled={!question.trim() || !answer.trim()} onClick={saveDraft}>保存待审稿</button></div>
          )}
        </div>
      </div>
    </div>
  );
}

/* ---- Single skill detail: six-segment tabs + suggestions + redline checker ---- */
function SkillDetail({ skill, onBack }) {
  const { SKILL_SEGMENTS, AI_DIFF } = window.DATA;
  const { Badge, Redline } = window;
  const Icons = window.Icons;
  const [seg, setSeg] = useState(1);
  const [segments, setSegments] = useState(SKILL_SEGMENTS);
  const [decisions, setDecisions] = useState({});
  const [rejecting, setRejecting] = useState(null);
  const [rejectReason, setRejectReason] = useState("");
  const [testState, setTestState] = useState("failed");
  const [generatedAt, setGeneratedAt] = useState("基于当前草稿生成");
  const [saved, setSaved] = useState(false);

  const segCur = segments.find(s => s.n === seg);
  const pending = AI_DIFF.filter((_, i) => !decisions[i]).length;
  const blockingPending = AI_DIFF.filter((d, i) => !decisions[i] && d.sev === "blocking").length;
  const unsafeIssues = [
    segments.find(item => item.n === 5)?.body.includes("24 小时内通过") ? "仍包含无依据的审核时限承诺" : "",
    !segments.find(item => item.n === 2)?.body.includes("不得承诺通过") ? "政策例外和绕过请求仍缺少明确边界" : "",
  ].filter(Boolean);
  const canSubmit = blockingPending === 0 && unsafeIssues.length === 0 && testState === "passed";
  const draftStepLabel = saved ? "等待发布" : testState === "passed" ? "测试通过" : "需要重测";

  const acceptSuggestion = (i) => {
    const suggestion = AI_DIFF[i];
    setSegments(items => items.map(item => item.n === suggestion.seg ? { ...item, body: suggestion.nextBody } : item));
    setDecisions(current => ({ ...current, [i]: { type: "accept" } }));
    setSeg(suggestion.seg);
    setTestState("stale");
    window.PROTOTYPE_STATE.skillDraftReady = false;
    window.PROTOTYPE_STATE.testSuitePassed = false;
    setSaved(false);
  };
  const startReject = (i) => {
    setRejecting(i);
    setRejectReason("");
  };
  const confirmReject = (i) => {
    if (!rejectReason.trim()) return;
    setDecisions(current => ({ ...current, [i]: { type: "reject", reason: rejectReason.trim() } }));
    setRejecting(null);
    setRejectReason("");
    setSaved(false);
  };
  const updateSegment = (value) => {
    setSegments(items => items.map(item => item.n === seg ? { ...item, body: value } : item));
    setTestState("stale");
    window.PROTOTYPE_STATE.skillDraftReady = false;
    window.PROTOTYPE_STATE.testSuitePassed = false;
    setSaved(false);
  };
  const runTests = () => {
    const ready = unsafeIssues.length === 0 && blockingPending === 0;
    setTestState(ready ? "passed" : "failed");
    window.PROTOTYPE_STATE.skillDraftReady = ready;
    window.PROTOTYPE_STATE.testSuitePassed = false;
    setSaved(false);
  };
  const refreshSuggestions = () => {
    setGeneratedAt("刚刚按当前草稿和最新依据检查");
  };

  return (
    <div className="content-pad fadein">
      <button className="btn sm ghost" onClick={onBack} style={{ marginBottom: 12 }}><Icons.chevR s={14} style={{ transform: "rotate(180deg)" }} /> 返回处理流程库</button>
      <div className="spread" style={{ marginBottom: 18, alignItems: "flex-start" }}>
        <div>
          <div className="row" style={{ gap: 10 }}>
            <h1 className="page-h1" style={{ fontSize: 19 }}>{skill.name}</h1>
            <Badge tone="gray">{skill.level}</Badge>
            <span className="muted">{skill.ver}</span>
          </div>
          <div className="page-sub">当前线上 {skill.liveVersion || skill.ver} · {skill.liveStatus || "已发布"} · 本次 {skill.ver} · {draftStepLabel} · 研发标识：<span className="mono">{skill.key}</span></div>
        </div>
        <div className="row" style={{ gap: 8 }}>
          <button className="btn sm" onClick={refreshSuggestions}><Icons.zap s={14} /> 重新生成建议</button>
          <button className="btn sm" onClick={runTests}><Icons.flask s={14} /> 重新测试</button>
          <button className="btn primary sm" disabled={!canSubmit} onClick={() => setSaved(true)}><Icons.checkCircle s={14} /> 保存并送审</button>
        </div>
      </div>

      <div className={"gate " + (canSubmit ? "" : "red")} style={{ marginBottom: 16 }}>
        <div className="gate-ico">{canSubmit ? <Icons.check s={17} /> : <Icons.lock s={17} sw={2} />}</div>
        <div style={{ flex: 1, fontWeight: 500 }}>
          {saved
            ? <><b>已送审。</b> 草稿、建议处理和测试结果已记录。</>
            : canSubmit
              ? <><b>可以送审。</b> 阻断项已处理，测试通过。</>
              : <><b>暂不能送审。</b> {blockingPending ? `还有 ${blockingPending} 条阻断建议未处理；` : ""}{unsafeIssues.join("；") || (testState === "stale" ? "内容已修改，需要重新测试" : "重新测试尚未通过")}。</>}
        </div>
      </div>

      <div className="sec-2col">
        {/* six-segment editor */}
        <div className="card">
          <div className="card-head"><Icons.doc s={15} /><span className="card-title">答案编辑器</span><span className="card-sub">六个部分</span></div>
          <div className="seg-tabs">
            {segments.map(s => (
              <button key={s.n} className={"seg-tab" + (seg === s.n ? " active" : "")} onClick={() => setSeg(s.n)}>
                <span className="seg-num">{s.n}</span>{s.title}
              </button>
            ))}
          </div>
          <div className="card-pad">
            <textarea className="seg-body seg-editor" aria-label={`编辑${segCur.title}`} value={segCur.body} onChange={event => updateSegment(event.target.value)} />
            {seg === 6 && (
              <div className="four-el">
                <div className="four-el-h">转人工后坐席获得的信息</div>
                <div className="four-el-grid">
                  {["客户", "问题", "已收集信息", "当前结论"].map((x, i) => (
                    <div key={i} className="four-el-chip"><Icons.check s={12} style={{ color: "var(--green)" }} /> {x}</div>
                  ))}
                </div>
              </div>
            )}
          </div>
        </div>

        {/* right: review suggestions + redline checker */}
        <div className="col" style={{ gap: 16 }}>
          {/* Review suggestions */}
          <div className="card">
            <div className="card-head"><Icons.zap s={15} /><span className="card-title">审稿建议</span><span className="card-sub">{generatedAt}</span>{pending > 0 ? <Badge tone="amber">{pending} 项待处理</Badge> : <Badge tone="green">已全部处理</Badge>}</div>
            <div className="card-pad" style={{ display: "flex", flexDirection: "column", gap: 10 }}>
              {AI_DIFF.map((d, i) => {
                const done = decisions[i];
                return (
                  <div key={i} className={"diff-card" + (done ? " resolved" : "")}>
                    <div className="spread" style={{ marginBottom: 6 }}>
                      <span className="row" style={{ gap: 7 }}>
                        <Badge tone={d.sev === "blocking" ? "red" : d.sev === "warning" ? "amber" : "gray"}>{d.sev === "blocking" ? "阻断" : d.sev === "warning" ? "警告" : "提示"}</Badge>
                        <span className="muted" style={{ fontSize: 12 }}>{d.sec}</span>
                      </span>
                      {done ? <Badge tone={done.type === "accept" ? "green" : "gray"}>{done.type === "accept" ? "已接受" : "已拒绝"}</Badge> : null}
                    </div>
                    <div className={"diff-line " + d.type}><span className="diff-sign">{d.type === "add" ? "+" : d.type === "del" ? "−" : "~"}</span>{d.text}</div>
                    <dl className="suggestion-detail-list compact">
                      <dt>触发原因</dt><dd>{d.trigger}</dd>
                      <dt>依据</dt><dd>{d.basis}</dd>
                      <dt>接受后</dt><dd>{d.effect}</dd>
                      <dt>需要重测</dt><dd>{d.tests}</dd>
                    </dl>
                    <div className="muted" style={{ fontSize: 12, margin: "7px 0" }}>{d.reason}</div>
                    {!done && (
                      <>
                        <div className="row" style={{ gap: 7 }}>
                          <button className="btn sm" style={{ padding: "4px 10px" }} onClick={() => acceptSuggestion(i)}><Icons.check s={13} /> 接受修改</button>
                          <button className="btn sm ghost" style={{ padding: "4px 10px" }} onClick={() => startReject(i)}>拒绝建议</button>
                        </div>
                        {rejecting === i ? (
                          <div className="reject-box">
                            <label>拒绝原因<input value={rejectReason} onChange={event => setRejectReason(event.target.value)} placeholder="说明为什么不采用；同一依据不会重复提出" /></label>
                            <div className="row" style={{ gap: 7 }}>
                              <button className="btn sm ghost" onClick={() => setRejecting(null)}>取消</button>
                              <button className="btn sm" disabled={!rejectReason.trim()} onClick={() => confirmReject(i)}>确认拒绝</button>
                            </div>
                          </div>
                        ) : null}
                      </>
                    )}
                    {done?.type === "reject" ? <div className="note"><Icons.alert s={13} /> 已拒绝：{done.reason}。{d.sev === "blocking" ? "草稿仍命中红线，继续阻断。" : "同一依据不再重复提出。"}</div> : null}
                  </div>
                );
              })}
            </div>
          </div>

          {/* redline checker */}
          <div className="card">
            <div className="card-head"><Icons.shield s={15} /><span className="card-title">安全检查</span></div>
            <div className="card-pad" style={{ display: "flex", flexDirection: "column", gap: 8 }}>
              {[
                { r: "不得承诺审核通过或完成时间", ok: !unsafeIssues.some(item => item.includes("时限承诺")) },
                { r: "政策例外和绕过请求有明确出口", ok: !unsafeIssues.some(item => item.includes("政策例外")) },
                { r: "禁止披露字段过滤", ok: true },
                { r: "安全敏感问题阻断", ok: true },
                { r: "未允许文章或待审核问答不得用于回答", ok: true },
                { r: "来源版本当前有效", ok: true },
                { r: "高风险结论由当前问答或处理流程覆盖", ok: true },
                { r: "个案表达与工具真实结果一致", ok: true },
                { r: "失败处理检查（每个无法继续的分支都有明确提示或转人工）", ok: true },
              ].map((c, i) => (
                <div key={i} className="spread rc-row">
                  <span style={{ fontSize: 12.5 }}>{c.r}</span>
                  {c.ok ? <Badge tone="green" dot="var(--green)">通过</Badge> : <Badge tone="red" dot="var(--red)">已阻断</Badge>}
                </div>
              ))}
              <div className="spread rc-row">
                <span style={{ fontSize: 12.5 }}>改动后重新测试</span>
                <Badge tone={testState === "passed" ? "green" : testState === "stale" ? "amber" : "red"}>{testState === "passed" ? "通过" : testState === "stale" ? "需要重测" : "失败"}</Badge>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

window.TrainView = TrainView;
