//! 人工审批节点辅助函数/类型 — 拒绝语义化关键字判定等纯逻辑
//!
//! 从 human_node.rs 抽离的纯函数/常量(execute 与关键字判定解耦,便于单测覆盖)。
//! HumanNode 的 struct + impl 仍保留在 human_node.rs(impl 块约束)。
//!
//! 拒绝语义化保留(executor set_failed → 推进链退回)。

/// 拒绝语义化关键字。
/// decision 归一化(去空白 + 小写)后命中此集合 → 审批拒绝 → 节点返 Err(触发工作流 failed)。
///
/// 识别范围（避免误伤）：
/// - 仅当节点 options 非空且该 decision ∈ options 时拒绝 —— 前端从受控选项中选择，语义可信；
/// - options 空(自由文本)时同样按关键字判定 —— 仅当文本明确为拒绝词才拒绝，
///   其余自由文本一律视为通过(向后兼容，不阻断自由反馈)。
///
/// 关键字集合与前端/IPC 的 options 命名约定对齐：「拒绝/驳回/退回」中文 +
/// 「reject/decline/deny/no/block」英文，覆盖默认 options `["同意","拒绝"]`。
pub(super) const REJECT_KEYWORDS: &[&str] = &[
    "拒绝", "驳回", "退回", "否决",
    "reject", "decline", "declined", "deny", "denied", "no", "block",
];

/// 判定单个 decision 是否为拒绝选项（归一化后命中 REJECT_KEYWORDS）。
pub(super) fn is_reject_decision(d: &str) -> bool {
    let normalized = d.trim().to_lowercase();
    REJECT_KEYWORDS.iter().any(|k| normalized == *k)
}

/// 判定 picked 集合是否含拒绝选项：任一项命中即视为整单拒绝（多选场景「选了驳回」即驳回）。
pub(super) fn contains_reject(picked: &[String]) -> bool {
    picked.iter().any(|d| is_reject_decision(d))
}
